mirror of https://github.com/lianthony/NT4.0
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.
635 lines
16 KiB
635 lines
16 KiB
/******************************Module*Header*******************************\
|
|
* Module Name: ldevobj.cxx *
|
|
* *
|
|
* Copyright (c) 1990-1994 Microsoft Corporation *
|
|
* *
|
|
* Pointers and locking are hidden in these objects. *
|
|
\**************************************************************************/
|
|
|
|
#include "precomp.hxx"
|
|
|
|
#define ENABLE_FUNC_NAME "DrvEnableDriver"
|
|
|
|
/******************************Member*Function*****************************\
|
|
* LDEVREF::LDEVREF (pszDriver,ldt)
|
|
*
|
|
* Locate an existing driver or load a new one. Increase its reference
|
|
* count.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
LDEVREF::LDEVREF(PWSZ pwszDriver, LDEVTYPE ldt) : XLDEVOBJ()
|
|
{
|
|
TRACE_INIT(("LDEVREF::LDEVREF: ENTERING\n"));
|
|
|
|
BOOL bLoaded;
|
|
|
|
//
|
|
// Assume failure.
|
|
//
|
|
|
|
pldev = NULL;
|
|
|
|
//
|
|
// Check for a bogus driver name.
|
|
//
|
|
|
|
if ((pwszDriver == (PWSZ) NULL) ||
|
|
(*pwszDriver == L'\0'))
|
|
{
|
|
WARNING("gdisrv!LDEVREF(): bogus driver name\n");
|
|
return;
|
|
}
|
|
|
|
#if DBG
|
|
//
|
|
// Check for bogus driver type
|
|
//
|
|
|
|
if ((ldt != LDEV_FONT) &&
|
|
(ldt != LDEV_DEVICE_DISPLAY) &&
|
|
(ldt != LDEV_DEVICE_PRINTER))
|
|
{
|
|
WARNING("gdisrv!LDEVREF(): bad LDEVTYPE\n");
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
NTSTATUS Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
pldev = ldevLoadImage(pwszDriver, FALSE, &bLoaded);
|
|
|
|
if (pldev)
|
|
{
|
|
if (bLoaded)
|
|
{
|
|
TRACE_INIT(("LDEVREF::LDEVREF: SUCCESS, Driver already loaded\n"));
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
|
|
DRVENABLEDATA ded = {0,0,(DRVFN *) NULL};
|
|
|
|
if ((pldev->pGdiDriverInfo->EntryPoint != NULL) &&
|
|
((PFN_DrvEnableDriver) pldev->pGdiDriverInfo->EntryPoint)(
|
|
ENGINE_VERSION, sizeof(DRVENABLEDATA), &ded) &&
|
|
(ded.iDriverVersion <= ENGINE_VERSION) &&
|
|
(ded.iDriverVersion >= ENGINE_VERSIONSUR) &&
|
|
bFillTable(ded))
|
|
{
|
|
//
|
|
// Make sure the name and type of the ldev is initialized
|
|
//
|
|
|
|
pldev->ldevType = ldt;
|
|
|
|
TRACE_INIT(("LDEVREF::LDEVREF: SUCCESS\n"));
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Error exit path
|
|
//
|
|
|
|
ldevUnloadImage(pldev);
|
|
|
|
pldev = NULL;
|
|
|
|
TRACE_INIT(("LDEVREF::LDEVREF: FAILIURE\n"));
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/******************************Member*Function*****************************\
|
|
* LDEVREF::LDEVREF
|
|
*
|
|
* Enable one of the statically linked font drivers via the LDEV.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
LDEVREF::LDEVREF(PFN pfnEnable,LDEVTYPE ldt) : XLDEVOBJ()
|
|
{
|
|
//
|
|
// Assume failure.
|
|
//
|
|
|
|
TRACE_INIT(("LDEVREF::LDEVREF: loading static font\n"));
|
|
|
|
//
|
|
// Allocate memory for the LDEV.
|
|
//
|
|
|
|
pldev = (LDEV *) PALLOCMEM(sizeof(LDEV), 'vdlG');
|
|
|
|
if (pldev == NULL)
|
|
{
|
|
WARNING("LDEV failed to allocate memory\n");
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Call the Enable entry point.
|
|
//
|
|
|
|
DRVENABLEDATA ded;
|
|
|
|
if (!((* (PFN_DrvEnableDriver) pfnEnable) (ENGINE_VERSION,sizeof(DRVENABLEDATA),&ded)))
|
|
{
|
|
VFREEMEM(pldev);
|
|
pldev = NULL;
|
|
WARNING("Static font driver init failed\n");
|
|
return;
|
|
}
|
|
|
|
pldev->ldevType = ldt;
|
|
pldev->cRefs = 1;
|
|
|
|
bFillTable(ded);
|
|
|
|
//
|
|
// Initialize the rest of the LDEV.
|
|
//
|
|
|
|
if (gpldevDrivers)
|
|
{
|
|
gpldevDrivers->pldevPrev = pldev;
|
|
}
|
|
|
|
pldev->pldevNext = gpldevDrivers;
|
|
pldev->pldevPrev = NULL;
|
|
|
|
gpldevDrivers = pldev;
|
|
|
|
//
|
|
// Since this driver is statically linked in, there is no name or
|
|
// MODOBJ.
|
|
//
|
|
|
|
pldev->pGdiDriverInfo = NULL;
|
|
|
|
TRACE_INIT(("LDEVREF::LDEVREF: SUCCESS loaded static font\n"));
|
|
|
|
}
|
|
|
|
/******************************Member*Function*****************************\
|
|
* LDEVREF::bFillTable (ded)
|
|
*
|
|
* Fills the dispatch table of the LDEV with function pointers from the
|
|
* driver. Checks that the required functions are present.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
#if DBG
|
|
static const ULONG aiFuncRequired[] =
|
|
{
|
|
INDEX_DrvEnablePDEV,
|
|
INDEX_DrvCompletePDEV,
|
|
INDEX_DrvDisablePDEV,
|
|
};
|
|
|
|
static const ULONG aiFuncPairs[][2] =
|
|
{
|
|
{INDEX_DrvCreateDeviceBitmap,INDEX_DrvDeleteDeviceBitmap}
|
|
};
|
|
static const ULONG aiFuncRequiredFD[] =
|
|
{
|
|
INDEX_DrvQueryFont,
|
|
INDEX_DrvQueryFontTree,
|
|
INDEX_DrvQueryFontData,
|
|
INDEX_DrvQueryFontCaps,
|
|
INDEX_DrvLoadFontFile,
|
|
INDEX_DrvUnloadFontFile,
|
|
INDEX_DrvQueryFontFile
|
|
};
|
|
#endif
|
|
|
|
BOOL LDEVREF::bFillTable(DRVENABLEDATA& ded)
|
|
{
|
|
//
|
|
// Get local copies of ded info and a pointer to the dispatch table.
|
|
//
|
|
|
|
ULONG cLeft = ded.c;
|
|
PDRVFN pdrvfn = ded.pdrvfn;
|
|
PFN *ppfnTable = pldev->apfn;
|
|
|
|
//
|
|
// Store the driver version in the LDEV
|
|
//
|
|
|
|
pldev->ulDriverVersion = ded.iDriverVersion;
|
|
|
|
//
|
|
// fill with zero pointers to avoid possibility of accessing
|
|
// incorrect fields later
|
|
//
|
|
|
|
RtlZeroMemory(ppfnTable, INDEX_LAST*sizeof(PFN));
|
|
|
|
//
|
|
// Copy driver functions into our table.
|
|
//
|
|
|
|
while (cLeft--)
|
|
{
|
|
//
|
|
// Check the range of the index.
|
|
//
|
|
|
|
if (pdrvfn->iFunc >= INDEX_LAST)
|
|
{
|
|
ASSERTGDI(FALSE,"bFillTableLDEVREF(): bogus function index\n");
|
|
return(FALSE);
|
|
}
|
|
|
|
//
|
|
// Copy the pointer.
|
|
//
|
|
|
|
ppfnTable[pdrvfn->iFunc] = pdrvfn->pfn;
|
|
pdrvfn++;
|
|
}
|
|
|
|
#if DBG
|
|
|
|
//
|
|
// Check for required driver functions.
|
|
//
|
|
|
|
cLeft = sizeof(aiFuncRequired) / sizeof(ULONG);
|
|
while (cLeft--)
|
|
{
|
|
if (ppfnTable[aiFuncRequired[cLeft]] == (PFN) NULL)
|
|
{
|
|
ASSERTGDI(FALSE,"bFillTableLDEVREF(): a required function is missing from driver\n");
|
|
return(FALSE);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check for required font functions.
|
|
//
|
|
|
|
if (pldev->ldevType == LDEV_FONT)
|
|
{
|
|
cLeft = sizeof(aiFuncRequiredFD) / sizeof(ULONG);
|
|
while (cLeft--)
|
|
{
|
|
if (ppfnTable[aiFuncRequiredFD[cLeft]] == (PFN) NULL)
|
|
{
|
|
ASSERTGDI(FALSE,"bFillTable(): a required FD function is missing\n");
|
|
return(FALSE);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check for functions that come in pairs.
|
|
//
|
|
|
|
cLeft = sizeof(aiFuncPairs) / sizeof(ULONG) / 2;
|
|
while (cLeft--)
|
|
{
|
|
//
|
|
// Make sure that either both functions are hooked or both functions
|
|
// are not hooked.
|
|
//
|
|
|
|
if ((ppfnTable[aiFuncPairs[cLeft][0]] == (PFN) NULL)
|
|
!= (ppfnTable[aiFuncPairs[cLeft][1]] == (PFN) NULL))
|
|
{
|
|
ASSERTGDI(FALSE,"bFillTableLDEVREF(): one of pair of functions is missing from driver\n");
|
|
return(FALSE);
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
/******************************Member*Function*****************************\
|
|
* LDEVREF::~LDEVREF ()
|
|
*
|
|
* Unlocks and possibly unload an LDEV.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
LDEVREF::~LDEVREF()
|
|
{
|
|
TRACE_INIT(("LDEVREF::~LDEVREF: ENTERING\n"));
|
|
|
|
if (pldev != NULL)
|
|
{
|
|
//
|
|
// Grab the semaphore th make sure everything is OK.
|
|
//
|
|
|
|
SEMOBJ so(gpsemDriverMgmt);
|
|
|
|
ldevUnloadImage(pldev);
|
|
}
|
|
|
|
TRACE_INIT(("LDEVREF::~LDEVREF: SUCCESS\n"));
|
|
|
|
}
|
|
|
|
/******************************Member*Function*****************************\
|
|
* PLDEV FindImage(PUNICODE_STRING pstrDriver)
|
|
*
|
|
* Determines if an image is already in the LDEV list.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
PLDEV
|
|
ldevLoadImage(
|
|
PWSZ pwszDriver,
|
|
BOOL bImage,
|
|
PBOOL pbAlreadyLoaded
|
|
)
|
|
{
|
|
|
|
PSYSTEM_GDI_DRIVER_INFORMATION pGdiDriverInfo = NULL;
|
|
PLDEV pldev = NULL;
|
|
|
|
UNICODE_STRING usDriverName;
|
|
PLDEV pldevList;
|
|
|
|
NTSTATUS Status;
|
|
BOOLEAN OldHardErrorMode;
|
|
|
|
TRACE_INIT(("ldevLoadImage called on Image %ws\n", pwszDriver));
|
|
|
|
*pbAlreadyLoaded = FALSE;
|
|
|
|
//
|
|
// Only append the .dll if it's NOT an image.
|
|
//
|
|
|
|
if (MakeSystemRelativePath(pwszDriver,
|
|
&usDriverName,
|
|
!bImage))
|
|
{
|
|
//
|
|
// Check both list of drivers.
|
|
//
|
|
|
|
VACQUIRESEM(gpsemDriverMgmt);
|
|
|
|
pldevList = gpldevDrivers;
|
|
|
|
TRACE_INIT(("ldevLoadImage - search for existing image %ws\n",
|
|
usDriverName.Buffer));
|
|
|
|
while (pldevList != NULL)
|
|
{
|
|
//
|
|
// If there is a valid driver image, and if the types are compatible.
|
|
// bImage == TRUE means load an image, while bImage == FALSE means
|
|
// anything else (for now)
|
|
//
|
|
|
|
if ((pldevList->pGdiDriverInfo) &&
|
|
((pldevList->ldevType == LDEV_IMAGE) == bImage))
|
|
{
|
|
//
|
|
// Do a case insensitive compare since the printer driver name
|
|
// can come from different locations.
|
|
//
|
|
|
|
if (RtlEqualUnicodeString(&(pldevList->pGdiDriverInfo->DriverName),
|
|
&usDriverName,
|
|
TRUE))
|
|
{
|
|
//
|
|
// If it's already loaded, increment the ref count
|
|
// and return that pointer.
|
|
//
|
|
|
|
TRACE_INIT(("ldevLoadImage found image. Inc ref count\n"));
|
|
|
|
pldevList->cRefs++;
|
|
|
|
*pbAlreadyLoaded = TRUE;
|
|
|
|
pldev = pldevList;
|
|
break;
|
|
}
|
|
}
|
|
|
|
pldevList = pldevList->pldevNext;
|
|
}
|
|
|
|
if (pldev == NULL)
|
|
{
|
|
TRACE_INIT(("ldevLoadImage - try to load new iamge\n"));
|
|
|
|
pGdiDriverInfo = (PSYSTEM_GDI_DRIVER_INFORMATION)
|
|
PALLOCNOZ(sizeof(SYSTEM_GDI_DRIVER_INFORMATION), 'idSG');
|
|
|
|
pldev = (PLDEV) PALLOCMEM(sizeof(LDEV), 'vdlG');
|
|
|
|
if (pGdiDriverInfo && pldev)
|
|
{
|
|
|
|
TRACE_INIT(("ldevLoadImage attempting to load new image\n"));
|
|
|
|
pGdiDriverInfo->DriverName = usDriverName;
|
|
|
|
//
|
|
// We must disable hard error popups when loading drivers.
|
|
// Otherwise we will deadlock since MM will directly try to put
|
|
// up a popup.
|
|
//
|
|
// This will also stop us from automatically bugchecking when
|
|
// an old driver is loaded, so we can try and recvoder from it.
|
|
//
|
|
// BUGBUG we want to put up our own popup if this occurs.
|
|
// It needs to be done higher up when we have no locks held.
|
|
//
|
|
|
|
OldHardErrorMode = PsGetCurrentThread()->HardErrorsAreDisabled;
|
|
PsGetCurrentThread()->HardErrorsAreDisabled = TRUE;
|
|
|
|
Status = ZwSetSystemInformation(SystemLoadGdiDriverInformation,
|
|
pGdiDriverInfo,
|
|
sizeof(SYSTEM_GDI_DRIVER_INFORMATION));
|
|
|
|
PsGetCurrentThread()->HardErrorsAreDisabled = OldHardErrorMode;
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
TRACE_INIT(("ldevLoadImage SUCCESS with HANDLE %08lx\n",
|
|
(ULONG)pGdiDriverInfo));
|
|
|
|
pldev->pGdiDriverInfo = pGdiDriverInfo;
|
|
pldev->cRefs = 1;
|
|
|
|
// Assume image for now.
|
|
|
|
pldev->ldevType = LDEV_IMAGE;
|
|
|
|
pldev->ulDriverVersion = (ULONG) -1;
|
|
|
|
if (gpldevDrivers)
|
|
{
|
|
gpldevDrivers->pldevPrev = pldev;
|
|
}
|
|
|
|
pldev->pldevNext = gpldevDrivers;
|
|
pldev->pldevPrev = NULL;
|
|
|
|
gpldevDrivers = pldev;
|
|
|
|
//
|
|
// We exit with all resources allocated, after leaving the
|
|
// semaphore.
|
|
//
|
|
|
|
VRELEASESEM(gpsemDriverMgmt);
|
|
return (pldev);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Check the special return code from MmLoadSystemImage
|
|
// that indicates this is an old driver being linked
|
|
// against something else than win32k.sys
|
|
//
|
|
// If it is, call user to log the error.
|
|
|
|
if (Status == STATUS_PROCEDURE_NOT_FOUND)
|
|
{
|
|
UserLogDisplayDriverEvent(MsgInvalidOldDriver);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Either success due to a cached entry, or failiure.
|
|
// In either case, we can free all the resources we allocatred.
|
|
//
|
|
|
|
if (pGdiDriverInfo)
|
|
VFREEMEM(pGdiDriverInfo);
|
|
|
|
if (pldev)
|
|
VFREEMEM(pldev);
|
|
|
|
pldev = NULL;
|
|
|
|
}
|
|
|
|
VRELEASESEM(gpsemDriverMgmt);
|
|
|
|
VFREEMEM(usDriverName.Buffer);
|
|
}
|
|
|
|
TRACE_INIT(("ldevLoadImage %ws with HANDLE %08lx\n",
|
|
pldev ? L"SUCCESS" : L"FAILED", pldev));
|
|
|
|
return (pldev);
|
|
|
|
}
|
|
|
|
|
|
/******************************Member*Function*****************************\
|
|
* ldevUnloadImage()
|
|
*
|
|
* Deletes an LDEV. Disables and unloads the driver.
|
|
*
|
|
* The reference count must be zero when this function is called.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
VOID
|
|
ldevUnloadImage(
|
|
PLDEV pldev
|
|
)
|
|
{
|
|
//
|
|
// Hold the LDEV semaphore until after the module is unloaded.
|
|
//
|
|
|
|
VACQUIRESEM(gpsemDriverMgmt);
|
|
|
|
if (--pldev->cRefs == 0)
|
|
{
|
|
//
|
|
// Make sure that there is exactly one reference to this LDEV.
|
|
//
|
|
|
|
ASSERTGDI(pldev->cRefs == 0, "ldevUnloadImage Ref Count not 0");
|
|
|
|
TRACE_INIT(("LDEVREF::bDelete: ENTERING\n"));
|
|
|
|
//
|
|
// If the module handle exits, need to unload the module. (Does not exist
|
|
// for the statically linked font drivers).
|
|
//
|
|
|
|
if (pldev->pGdiDriverInfo)
|
|
{
|
|
//
|
|
// Disable the driver.
|
|
//
|
|
|
|
TRACE_INIT(("LDEVREF::bDelete: calling the driver to unload\n"));
|
|
|
|
//
|
|
// Tell the module to unload.
|
|
//
|
|
|
|
TRACE_INIT(("ldevUnloadImage called on Image %08lx, \n %ws\n",
|
|
(ULONG) pldev, pldev->pGdiDriverInfo->DriverName.Buffer));
|
|
|
|
ZwSetSystemInformation(SystemUnloadGdiDriverInformation,
|
|
&(pldev->pGdiDriverInfo->SectionPointer),
|
|
sizeof(ULONG));
|
|
|
|
//
|
|
// Free the memory associate with the module
|
|
//
|
|
|
|
VFREEMEM(pldev->pGdiDriverInfo->DriverName.Buffer);
|
|
VFREEMEM(pldev->pGdiDriverInfo);
|
|
|
|
}
|
|
|
|
//
|
|
// Remove the ldev from the linker list
|
|
//
|
|
|
|
if (pldev->pldevNext)
|
|
{
|
|
pldev->pldevNext->pldevPrev = pldev->pldevPrev;
|
|
}
|
|
|
|
if (pldev->pldevPrev)
|
|
{
|
|
pldev->pldevPrev->pldevNext = pldev->pldevNext;
|
|
}
|
|
else
|
|
{
|
|
gpldevDrivers = pldev->pldevNext;
|
|
}
|
|
|
|
//
|
|
// Free the ldev
|
|
//
|
|
|
|
VFREEMEM(pldev);
|
|
}
|
|
else
|
|
{
|
|
TRACE_INIT(("ldevUnloadImage - refcount decremented\n"));
|
|
}
|
|
|
|
VRELEASESEM(gpsemDriverMgmt);
|
|
|
|
return;
|
|
}
|