|
|
/******************************Module*Header*******************************\
* Module Name: spool.cxx * * Created: 21-Feb-1995 10:13:18 * Author: Eric Kutter [erick] * * Copyright (c) 1993-1999 Microsoft Corporation * \**************************************************************************/
#include "precomp.hxx"
DWORD DriverInfo1Offsets[]={offsetof(DRIVER_INFO_1W, pName), 0xFFFFFFFF};
DWORD DriverInfo2Offsets[]={offsetof(DRIVER_INFO_2W, pName), offsetof(DRIVER_INFO_2W, pEnvironment), offsetof(DRIVER_INFO_2W, pDriverPath), offsetof(DRIVER_INFO_2W, pDataFile), offsetof(DRIVER_INFO_2W, pConfigFile), 0xFFFFFFFF}; DWORD DriverInfo3Offsets[]={offsetof(DRIVER_INFO_3W, pName), offsetof(DRIVER_INFO_3W, pEnvironment), offsetof(DRIVER_INFO_3W, pDriverPath), offsetof(DRIVER_INFO_3W, pDataFile), offsetof(DRIVER_INFO_3W, pConfigFile), offsetof(DRIVER_INFO_3W, pHelpFile), offsetof(DRIVER_INFO_3W, pDependentFiles), offsetof(DRIVER_INFO_3W, pMonitorName), offsetof(DRIVER_INFO_3W, pDefaultDataType), 0xFFFFFFFF};
DWORD DriverInfo1Strings[]={offsetof(DRIVER_INFO_1W, pName), 0xFFFFFFFF};
DWORD DriverInfo2Strings[]={offsetof(DRIVER_INFO_2W, pName), offsetof(DRIVER_INFO_2W, pEnvironment), offsetof(DRIVER_INFO_2W, pDriverPath), offsetof(DRIVER_INFO_2W, pDataFile), offsetof(DRIVER_INFO_2W, pConfigFile), 0xFFFFFFFF}; DWORD DriverInfo3Strings[]={offsetof(DRIVER_INFO_3W, pName), offsetof(DRIVER_INFO_3W, pEnvironment), offsetof(DRIVER_INFO_3W, pDriverPath), offsetof(DRIVER_INFO_3W, pDataFile), offsetof(DRIVER_INFO_3W, pConfigFile), offsetof(DRIVER_INFO_3W, pHelpFile), offsetof(DRIVER_INFO_3W, pMonitorName), offsetof(DRIVER_INFO_3W, pDefaultDataType), 0xFFFFFFFF};
DWORD FormInfo1Offsets[] = { offsetof(FORM_INFO_1W, pName), 0xFFFFFFFF};
DWORD PrinterInfo1Offsets[]={offsetof(PRINTER_INFO_1W, pDescription), offsetof(PRINTER_INFO_1W, pName), offsetof(PRINTER_INFO_1W, pComment), 0xFFFFFFFF};
DWORD PrinterInfo2Offsets[]={offsetof(PRINTER_INFO_2W, pServerName), offsetof(PRINTER_INFO_2W, pPrinterName), offsetof(PRINTER_INFO_2W, pShareName), offsetof(PRINTER_INFO_2W, pPortName), offsetof(PRINTER_INFO_2W, pDriverName), offsetof(PRINTER_INFO_2W, pComment), offsetof(PRINTER_INFO_2W, pLocation), offsetof(PRINTER_INFO_2W, pDevMode), offsetof(PRINTER_INFO_2W, pSepFile), offsetof(PRINTER_INFO_2W, pPrintProcessor), offsetof(PRINTER_INFO_2W, pDatatype), offsetof(PRINTER_INFO_2W, pParameters), offsetof(PRINTER_INFO_2W, pSecurityDescriptor), 0xFFFFFFFF};
DWORD PrinterInfo3Offsets[]={offsetof(PRINTER_INFO_3, pSecurityDescriptor), 0xFFFFFFFF};
DWORD PrinterInfo4Offsets[]={offsetof(PRINTER_INFO_4W, pPrinterName), offsetof(PRINTER_INFO_4W, pServerName), 0xFFFFFFFF};
DWORD PrinterInfo5Offsets[]={offsetof(PRINTER_INFO_5W, pPrinterName), offsetof(PRINTER_INFO_5W, pPortName), 0xFFFFFFFF};
/*********************************Class************************************\
* SPOOLMSG * * structure containg a message waiting to be grabbed by a spooler thread. * This is a private structure to communicate between the applications thread * and the spoolers thread * * History: * 27-Mar-1995 -by- Eric Kutter [erick] * Wrote it. \**************************************************************************/
#define SPOOLMSG_DELETE_INBUF 0x00000002
#define SPOOLMSG_MAXINPUT_BUF 4
#define NO_CLIENT 1
#define NON_SPOOL_INSTANCE ((DWORD) -1)
#define MIN_DEVMODE_SIZE 72 // Win 3.1 DevMode size. Also hard-coded in yspool
typedef struct _SPOOLMSG { ULONG cj; ULONG iMsg; ULONG fl;
HSPOOLOBJ hso; PETHREAD pthreadClient; SECURITY_CLIENT_CONTEXT sccSecurity;
// in order to avoid extra copying into a single buffer, there are multiple
// input buffers. For example, WritePrinter uses a header buffer on the
// stack with a second buffer containing the output data.
ULONG cjIn; // combined size of input buffers
ULONG cBuf; // number of input buffers
PULONG apulIn[SPOOLMSG_MAXINPUT_BUF];// input buffers
ULONG acjIn[SPOOLMSG_MAXINPUT_BUF];
PULONG pulOut; // location to put output data
ULONG cjOut; // size of output
ULONG ulRet; // return value
ULONG ulErr; // transfer last error from spooler thread to client
struct _SPOOLMSG *pNext;
} SPOOLMSG, *PSPOOLMSG;
/*********************************Class************************************\
* SPOOLOBJ : public OBJECT * * a SPOOLOBJ is GDI's internal spooler object containing data need to access * the spooler for a particular print job. * * Public Interface: * * History: * 21-Jun-1995 -by- Eric Kutter [erick] * Wrote it. \**************************************************************************/
class SPOOLOBJ : public OBJECT { public:
KEVENT *pkevent; HANDLE hSpool; PVOID pvMsg; SPOOLMSG sm; DWORD dwFlags; DWORD dwSpoolInstance; GREWRITEPRINTER WritePrinter; DWORD dwWritePrinterReturn; }; typedef SPOOLOBJ *PSPOOLOBJ;
/**************************************************************************\
* \**************************************************************************/
BOOL gbInitSpool = FALSE; // set/cleared as the spooler comes and goes
PW32PROCESS gpidSpool = 0; // process ID of the spooler
PEPROCESS gpeSpool = 0; // process pointer of the spooler
PKEVENT gpeventGdiSpool; // spooler lock
PKEVENT gpeventSpoolerTermination; // signals spooler termination so client threads in spooler process can exit
DWORD gdwSpoolInstance = 0; // Keeps track of the spooler instance
LONG gpInfiniteWait = TRUE; // LONG used to ensure we have one spooler thread waiting with INFINITE timeoue
// there is a queue of spool messages. Elements are removed from the head
// and added to the tail
PSPOOLMSG gpsmSpoolHead = NULL; PSPOOLMSG gpsmSpoolTail = NULL;
int gcSpoolMsg = 0; int gcSpoolMsgCurrent = 0; int gcSpoolMsgMax = 0;
#define LOCKSPOOLER GreAcquireSemaphore(ghsemGdiSpool)
#define UNLOCKSPOOLER GreReleaseSemaphore(ghsemGdiSpool)
#define POFFSET(pBase,pCur) ((PBYTE)(pCur) - (PBYTE)(pBase))
/*********************************Class************************************\
* class SPOOLREF * * Public Interface: * * History: * 22-May-1995 -by- Eric Kutter [erick] * Wrote it. \**************************************************************************/
class SPOOLREF { public:
PSPOOLOBJ pso;
SPOOLREF() {} SPOOLREF(HANDLE hspool) { pso = (PSPOOLOBJ)HmgLock((HOBJ)hspool,SPOOL_TYPE); }
~SPOOLREF() { if (bValid()) { DEC_EXCLUSIVE_REF_CNT(pso); pso = NULL; } }
BOOL bDelete(); BOOL GreEscapeSpool();
BOOL bValid() {return(pso != NULL); } HSPOOLOBJ hGet() {return((HSPOOLOBJ)pso->hGet());} PSPOOLMSG psm() {return(&pso->sm); }
// we need to know the lock count for cleanup so we don't free up a message
// when it still may be accessed.
ULONG cAltLock() { return(((POBJ) (pso))->ulShareCount); } };
class SPOOLALTREF : public SPOOLREF { public:
SPOOLALTREF(HANDLE hspool) { pso = (PSPOOLOBJ)HmgShareLock((HOBJ)hspool,SPOOL_TYPE); }
~SPOOLALTREF() { if (bValid()) { DEC_SHARE_REF_CNT(pso); pso = NULL; } } };
typedef SPOOLALTREF *PSPOOLALTREF;
class SPOOLMEMOBJ : public SPOOLREF { public:
SPOOLMEMOBJ(); ~SPOOLMEMOBJ() {} };
ULONG ulFinishMessage( PSPOOLMSG psm, PSPOOLESC psesc, PSPOOLALTREF sr, PBYTE pjEscData, ULONG cjEscData );
BOOL AddMessage2Q( PSPOOLMSG psm, DWORD dwSpoolInstance );
VOID SubtractMessageFromQ(PSPOOLMSG psmIn);
/******************************Public*Routine******************************\
* * * History: * 22-May-1995 -by- Eric Kutter [erick] * Wrote it. \**************************************************************************/
SPOOLMEMOBJ::SPOOLMEMOBJ() { KEVENT *pkevent = (PKEVENT) GdiAllocPoolNonPagedNS( sizeof(KEVENT), 'lpsG');
if (pkevent == NULL) { pso = NULL; } else { pso = (PSPOOLOBJ)HmgAlloc(sizeof(SPOOLOBJ),SPOOL_TYPE, HMGR_ALLOC_LOCK);
if (bValid()) {
LOCKSPOOLER; pso->dwSpoolInstance = gdwSpoolInstance; UNLOCKSPOOLER;
pso->pkevent = pkevent;
KeInitializeEvent( pso->pkevent, SynchronizationEvent, FALSE); } else { VFREEMEM(pkevent); } } }
/******************************Public*Routine******************************\
* * * History: * 22-May-1995 -by- Eric Kutter [erick] * Wrote it. \**************************************************************************/
BOOL SPOOLREF::bDelete() { if (bValid()) { VFREEMEM(pso->pkevent);
HmgFree((HOBJ)hGet());
pso = NULL; } return(TRUE); }
/******************************Public*Routine******************************\
* bIsProcessLocalSystem() * * History: * 19-Jun-2001 -by- Barton House [bhouse] * Wrote it. \**************************************************************************/
static BOOL bIsProcessLocalSystem(void) { BOOL bResult = FALSE; PEPROCESS peprocess; PACCESS_TOKEN paccessToken; NTSTATUS status; PTOKEN_USER ptu;
peprocess = PsGetCurrentProcess();
paccessToken = PsReferencePrimaryToken(peprocess);
status = SeQueryInformationToken(paccessToken, TokenUser, (PVOID *) &ptu);
PsDereferencePrimaryToken(paccessToken);
if (NT_SUCCESS(status) == TRUE) { bResult = RtlEqualSid(SeExports->SeLocalSystemSid, ptu->User.Sid); ExFreePool(ptu); }
return bResult; }
/******************************Public*Routine******************************\
* GreInitSpool() * * History: * 21-Feb-1995 -by- Eric Kutter [erick] * Wrote it. \**************************************************************************/
BOOL NtGdiInitSpool() { BOOL bRet = FALSE;
LOCKSPOOLER;
if (gbInitSpool) { EngSetLastError(ERROR_INVALID_ACCESS); WARNING("GreInitSpool - already called\n"); } else if(!bIsProcessLocalSystem()) { EngSetLastError(ERROR_INVALID_ACCESS); WARNING("GreInitSpool - caller is not system\n"); } else { NTSTATUS status;
// intialize the spooler events
gpeventGdiSpool = (PKEVENT) GdiAllocPoolNonPagedNS( sizeof(KEVENT), 'gdis');
gpeventSpoolerTermination = (PKEVENT) GdiAllocPoolNonPagedNS( sizeof(KEVENT), 'gdis');
if (gpeventGdiSpool && gpeventSpoolerTermination) { KeInitializeEvent( gpeventGdiSpool, SynchronizationEvent, FALSE);
KeInitializeEvent( gpeventSpoolerTermination, NotificationEvent, FALSE);
gbInitSpool = TRUE; bRet = TRUE; gpeSpool = PsGetCurrentProcess();
} else { EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
if (gpeventGdiSpool) { VFREEMEM(gpeventGdiSpool); gpeventGdiSpool = NULL; }
if (gpeventSpoolerTermination) { VFREEMEM(gpeventSpoolerTermination); gpeventSpoolerTermination = NULL; } }
gpidSpool = W32GetCurrentProcess();
if (++gdwSpoolInstance == NON_SPOOL_INSTANCE) ++gdwSpoolInstance; }
UNLOCKSPOOLER;
return(bRet); }
/******************************Public*Routine******************************\
* * * History: * 01-Jun-1995 -by- Eric Kutter [erick] * Wrote it. \**************************************************************************/
VOID vCleanupSpool() { if (gpidSpool == W32GetCurrentProcess()) { //DbgPrint("vCleanupSpool() - cleaning up the spooler process\n");
LOCKSPOOLER;
if (gbInitSpool == TRUE) {
while (gpsmSpoolHead != NULL) { //DbgPrint("vCleanupSpool() - got another\n");
PSPOOLMSG psm = gpsmSpoolHead;
gpsmSpoolHead = psm->pNext;
// and some stats
ASSERTGDI(gcSpoolMsgCurrent > 0, "GreGetSpoolMsg - invalid count\n"); gcSpoolMsgCurrent--;
SPOOLALTREF sr(psm->hso);
if (sr.bValid()) { KeSetEvent(sr.pso->pkevent,0,FALSE); } }
gpsmSpoolTail = NULL;
VFREEMEM(gpeventGdiSpool); VFREEMEM(gpeventSpoolerTermination);
gpeventGdiSpool = NULL; gpeventSpoolerTermination = NULL; gbInitSpool = FALSE; gpeSpool = NULL;
gcSpoolMsg = 0; gcSpoolMsgCurrent = 0; gcSpoolMsgMax = 0;
gpidSpool = NULL;
//DbgPrint("Done cleaning up spooler for this thread\n");
}
UNLOCKSPOOLER; } }
/******************************Public*Routine******************************\
* GreGetSpoolMessage() * * This is intended to be called from the spooler process (GdiGetSpoolMessage) * to get the next spooler message out of the kernel. * * * if (output buffer exists) * copy out output buffer * * wait for next message * * copy in input buffer * * input: * * psesc - buffer to place message * cjmsg - size of message buffer * pulOut - buffer containing data to be copied to output buffer * cjOut - size of output buffer * * * returns: * * size of data placed in psesc. * * * Note: the output buffer is filled by the calling routine before calling * this function again. * * History: * 21-Feb-1995 -by- Eric Kutter [erick] * * 6-Aug-1995 - Added Input buffer growing (Steve Wilson [swilson]) * * Wrote it. \**************************************************************************/
SECURITY_QUALITY_OF_SERVICE Sqos = { sizeof(SECURITY_QUALITY_OF_SERVICE), SecurityImpersonation, SECURITY_DYNAMIC_TRACKING, FALSE };
ULONG GreGetSpoolMessage( PSPOOLESC psesc, PBYTE pjEscData, // this must only be accessed under a try/except
ULONG cjEscData, PULONG pulOut, ULONG cjOut ) { ULONG ulRet = 0; NTSTATUS status; LONG lState;
//DbgPrint("Entered GreGetSpoolMessage\n");
if (!gbInitSpool) { WARNING("GreGetSpoolMessage - not initialized\n"); } else if (gpidSpool != W32GetCurrentProcess()) { WARNING("GreGetSpoolMessage - called from non-spooler process\n"); } else { KEVENT *pkevent = NULL;
// see if we need to copy any data out
if (psesc->hso) { SPOOLALTREF sr(psesc->hso);
if (sr.bValid()) {
// we have found the spool obj. Now see if we really have
// an output buffer and need to copy anything.
PSPOOLMSG psm = (PSPOOLMSG)sr.pso->pvMsg;
// if we asked the spooler to grow the buffer and it succeeded
// finish copying the message and return. If it failed, we still
// need to cleanup this message and release the thread.
if (psesc->iMsg == GDISPOOL_INPUT2SMALL) { if (psesc->ulRet && psm) { return(ulFinishMessage(psm, psesc, &sr, pjEscData, cjEscData)); }
pulOut = NULL; psesc->ulRet = 0; }
// if we actualy have a message, we need to copy out the data
// if there is any and remember the event in order to let the
// client continue.
if (psm) { // if we are still impersonating the client, undo it
if (psm->pthreadClient) { SeStopImpersonatingClient(); SeDeleteClientSecurity(&psm->sccSecurity); psm->pthreadClient = NULL; }
// see if we have anything to copy out
if (pulOut && psm->pulOut) { if (cjOut > psm->cjOut) cjOut = psm->cjOut;
__try { // this is the only place that pulOut is validated
ProbeForRead(pulOut,cjOut,sizeof(ULONG));
RtlCopyMemory(psm->pulOut,pulOut,cjOut);
psm->cjOut = cjOut; } __except(EXCEPTION_EXECUTE_HANDLER) { psm->cjOut = 0; } } else { psm->cjOut = 0; }
psm->ulRet = psesc->ulRet; psm->ulErr = EngGetLastError();
// Since to have output the message must have been synchronous,
// we need to let the other thread go now.
pkevent = sr.pso->pkevent; }
// the spool obj is now done with this message
sr.pso->pvMsg = NULL; } else { WARNING("GreGetSpoolMessage - hso but no pso\n"); } }
// the release of the other thread needs to be done once the SPOOLOBJ has
// been unlocked.
if (pkevent) { lState = KeSetEvent(pkevent,0,FALSE);
ASSERTGDI(lState == 0,"GreGetSpoolMessage - lState not 0, a\n"); }
// done with the last message. Wait for the next message.
PSPOOLMSG psm = NULL; BOOL bDone; LARGE_INTEGER Timeout;
// 600/(100*10(-9)) : negative value is interval, positive is absolute
Timeout.QuadPart = (LONGLONG) -6000000000; // Timeout is in 100 nsec, so 6,000,000,000 == 10 min
do { BOOL bGrabIt = TRUE; bDone = TRUE;
if (gpsmSpoolHead == NULL) { LONG bInfiniteWait = InterlockedExchange(&gpInfiniteWait, FALSE);
//DbgPrint("\nGDISPOOL(%lx): waiting for message\n",psesc);
status = KeWaitForSingleObject( gpeventGdiSpool, (KWAIT_REASON)WrExecutive, UserMode, FALSE, (bInfiniteWait ? NULL : &Timeout));
if(bInfiniteWait) InterlockedExchange(&gpInfiniteWait, TRUE);
if (status == STATUS_TIMEOUT) { SendSimpleMessage(0, GDISPOOL_TERMINATETHREAD, NON_SPOOL_INSTANCE); } else if (status == STATUS_USER_APC) // Upon this return, User mode spooler does not execute
{ KeSetEvent(gpeventSpoolerTermination,0,FALSE); return (ULONG) -1; } else if (!NT_SUCCESS(status)) { WARNING("GDISPOOL: wait error\n"); bGrabIt = FALSE; } } else { //DbgPrint("\nGDISPOOL(%lx): message already there\n",psesc);
}
if (bGrabIt) { // now find the message and the spoolobj
LOCKSPOOLER;
if (gpsmSpoolHead == NULL) { bDone = FALSE; } else { psm = gpsmSpoolHead; gpsmSpoolHead = psm->pNext;
if (gpsmSpoolHead == NULL) gpsmSpoolTail = NULL;
// and some stats
ASSERTGDI(gcSpoolMsgCurrent > 0, "GreGetSpoolMsg - invalid count\n"); gcSpoolMsgCurrent--; //DbgPrint(" got a message(%lx), hso = %lx - count = %ld\n",psesc,psm->hso,gcSpoolMsgCurrent);
}
UNLOCKSPOOLER; }
} while ((psm == NULL) && !bDone);
// did we get a message?
if (psm != NULL) { if (psm->iMsg == GDISPOOL_TERMINATETHREAD || psm->iMsg == GDISPOOL_CLOSEPRINTER) { // going to terminate the thread so just get out.
ulRet = sizeof(SPOOLESC); psesc->cj = sizeof(SPOOLESC); psesc->iMsg = psm->iMsg; psesc->hso = psm->hso;
if (psm->iMsg & GDISPOOL_API) // Let API messages have a spool handle
{ psesc->hSpool = psm->hso;
if (psm->iMsg & GDISPOOL_CLOSEPRINTER) { psesc->hso = NULL; } }
psesc->cjOut = 0; VFREEMEM(psm); } else // Got a non-null, non-TERMINATETHREAD message to send to spooler
{ SPOOLALTREF sr(psm->hso);
if (sr.bValid()) { if (cjEscData < psm->cjIn) { // set up the header
ulRet = offsetof(SPOOLESC, ajData);
psesc->cj = sizeof(SPOOLESC); psesc->iMsg = GDISPOOL_INPUT2SMALL; psesc->hSpool = sr.pso->hSpool; psesc->hso = psm->hso; sr.pso->pvMsg = (PVOID)psm;
// required message buffer size
psesc->cjOut = psm->cjIn + offsetof(SPOOLESC,ajData); } else { ulRet = ulFinishMessage(psm, psesc, &sr, pjEscData, cjEscData); } } } } }
return(ulRet); }
/******************************Public*Routine******************************\
* ulFinishMessage() * * Fills in psesc structure and impersonates client * * * History: * 8-Aug-95 -by- Steve Wilson [swilson] * Wrote it. \**************************************************************************/
ULONG ulFinishMessage( PSPOOLMSG psm, PSPOOLESC psesc, // this must only be accessed under a try/except
PSPOOLALTREF psr, PBYTE pjEscData, ULONG cjEscDAta ) { NTSTATUS status; ULONG ulRet;
// impersonate the client
status = SeCreateClientSecurity( psm->pthreadClient, &Sqos, FALSE, &psm->sccSecurity);
if (NT_SUCCESS(status)) { status = SeImpersonateClientEx(&psm->sccSecurity,NULL); }
if (!NT_SUCCESS(status)) { WARNING("FinishMessage - CreateClientSecurity failed\n"); psm->pthreadClient = NULL; }
// copy the data
ulRet = 0;
if (psm->cjIn) { __try { // copy all the buffers into the input buffer
ulRet = 0;
for (DWORD i = 0; i < psm->cBuf; ++i) { RtlCopyMemory(pjEscData+ulRet,psm->apulIn[i],psm->acjIn[i]); ulRet += psm->acjIn[i]; }
ASSERTGDI(ulRet == psm->cjIn,"ulFinishMessage - invalid size\n"); } __except(EXCEPTION_EXECUTE_HANDLER) { ulRet = 0; } }
// if the input buffer was only temporary, delete it.
if (psm->fl & SPOOLMSG_DELETE_INBUF) { ASSERTGDI(psm->cBuf == 1,"ulFinishMessage - delete more than 1\n");
VFREEMEM(psm->apulIn[0]); psm->apulIn[0] = NULL; }
// set up the header
ulRet += offsetof(SPOOLESC,ajData);
psesc->iMsg = psm->iMsg; psesc->cj = ulRet; psesc->hSpool = psr->pso->hSpool; psesc->cjOut = psm->cjOut;
psesc->hso = psm->hso; psr->pso->pvMsg = (PVOID)psm;
return(ulRet); }
/******************************Public*Routine******************************\
* GreEscapeSpool() * * given a spool message, add it to the queue and notify the spooler thread * * * History: * 21-Feb-1995 -by- Eric Kutter [erick] * Wrote it. \**************************************************************************/
BOOL SPOOLREF::GreEscapeSpool() { BOOL bRet = FALSE; NTSTATUS status;
ASSERTGDI(psm()->iMsg != GDISPOOL_TERMINATETHREAD,"GreEscapeSpool - GDISPOOL_TERMINATETHREAD\n"); ASSERTGDI(psm()->iMsg != GDISPOOL_CLOSEPRINTER,"GreEscapeSpool - GDISPOOL_CLOSEPRINTER\n"); ASSERTGDI(psm() != NULL,"GreEscapeSpool - null\n");
//DbgPrint("Entered GreEscapeSpool\n");
if (!gbInitSpool) { EngSetLastError(ERROR_NOT_READY); WARNING("GreEscapeSpool - not initialized\n"); } else if (pso->dwFlags & NO_CLIENT) { //DbgPrint(" GreEscapeSpool: NO_CLIENT, message received: %x\n",psm()->iMsg);
EngSetLastError(ERROR_PROCESS_ABORTED); } else { // add the message to the queue
//NOTE: we may want to reference count this in the future
psm()->pthreadClient = PsGetCurrentThread(); if (AddMessage2Q(psm(), pso->dwSpoolInstance)) {
PVOID pEvent[3];
pEvent[0] = gpeSpool; pEvent[1] = pso->pkevent; pEvent[2] = gpeventSpoolerTermination;
status = KeWaitForMultipleObjects( 3, pEvent, WaitAny, (KWAIT_REASON)0, UserMode, FALSE, 0, NULL);
switch (status) { case 0: // Spooler terminated
SubtractMessageFromQ(psm()); EngSetLastError(ERROR_PROCESS_ABORTED);
ASSERTGDI(cAltLock() == 0,"GreEscapeSpool - invalid lock 0\n"); bRet = FALSE; break;
case 1: // Spooler returned
bRet = TRUE; EngSetLastError(psm()->ulErr);
ASSERTGDI(cAltLock() == 0,"GreEscapeSpool - invalid lock 1\n");
break;
case STATUS_USER_APC: // Client terminated
case 2: // Spooler terminated, this client may be the spooler
// Stress Failure Note:
// AddMessage2Q is called above here to add a message to the message queue.
// After the message is in the queue, we leave the spooler lock and set
// gpeventGdiSpool, which wakes up the spooler thread. The spooler thread
// then grabs the message and removes it from the message queue inside the spooler
// lock. It then returns to user mode and creates a new message thread. All this
// typically works fine.
//
// Now suppose just after AddMessage2Q adds a new message and before gpeventGdiSpool
// is set, the spooler shuts down. When the spooler shuts down, STATUS_USER_APC is
// returned from the gpeventGdiSpool Wait and all spooler messaging threads will set
// gpeventSpoolerTermination, which is case 2 in this switch statement. This wakes
// up the client thread, which proceeds to terminate and frees the psm before exiting.
// Meanwhile, the spooler is down to its very last thread and calls vCleanupSpool, which
// traverses and frees the psm queue. vCleanupSpool will AV when it hits one of the freed
// messages in the queue.
//
// The problem was rarely encountered because it would only occur if the spooler shut down
// after a message was entered in the queue and before the gpeventGdiSpool event was seen.
//
// Removing the message from the queue when the spooler or client terminates should solve
// the problem. Note that this was always done for the STATUS_USER_APC case, and has now
// been added to case 0 and case 2.
//
// Steve Wilson (NT)
//
SubtractMessageFromQ(psm());
WARNING("Win32K spool: Client is dying!\n");
pso->dwFlags |= NO_CLIENT; EngSetLastError(ERROR_PROCESS_ABORTED); bRet = FALSE;
// we need to make sure there is no alt lock
pso->pvMsg = NULL;
while (cAltLock()) KeDelayExecutionThread(KernelMode,FALSE,gpLockShortDelay);
break;
default: #if 1
DbgPrint("GreEscapeSpool - WaitForSingleObject failed w/ status 0x%lx\n", status); DbgBreakPoint(); #else
WARNING("GreEscapeSpool - WaitForSingleObject failed\n"); #endif
break; } } }
return bRet; }
/******************************Public*Routine******************************\
* SendSimpleMessage() * * allow a client app to send a spool message * * * History: * 21-Feb-1995 -by- Eric Kutter [erick] * 7-Jun-1996 - Steve Wilson [SWilson] - Changed from NtGdiSpoolEsc to SendSimpleMessage * \**************************************************************************/
ULONG SendSimpleMessage( HANDLE hSpool, ULONG iMsg, DWORD dwSpoolInstance) { ULONG ulRet = 0;
if (!gbInitSpool) { WARNING("GreEscapeSpool - not initialized\n"); } else if (iMsg == GDISPOOL_TERMINATETHREAD || iMsg == GDISPOOL_CLOSEPRINTER) { PSPOOLMSG psm = (PSPOOLMSG) PALLOCMEM(sizeof(SPOOLMSG),'lpsG');
if (psm) { psm->cj = sizeof(SPOOLMSG); psm->iMsg = iMsg; psm->hso = (HSPOOLOBJ) hSpool; // This is Spooler's handle, not kernel handle
ulRet = AddMessage2Q(psm, dwSpoolInstance); if (ulRet == FALSE) VFREEMEM(psm); } }
return ulRet; }
/*****************************
SubtractMessageFromQ() *****************************/
VOID SubtractMessageFromQ(PSPOOLMSG psmIn) { PSPOOLMSG psm; PSPOOLMSG psmPrev = NULL;
//DbgPrint("Enter SubtractMessageFromQ\n");
LOCKSPOOLER;
for (psm = gpsmSpoolHead ; psm ; psmPrev = psm , psm = psm->pNext) { if (psm == psmIn) { if (psmPrev) { psmPrev->pNext = psm->pNext;
if (!psmPrev->pNext) { ASSERTGDI(psm == gpsmSpoolTail,"SubtractMessageFromQ: Bad gpsmSpoolTail!\n"); gpsmSpoolTail = psmPrev; } } else { gpsmSpoolHead = psm->pNext;
if (!gpsmSpoolHead) { ASSERTGDI(psm == gpsmSpoolTail,"SubtractMessageFromQ: Bad gpsmSpoolTail!\n"); gpsmSpoolTail = gpsmSpoolHead; } }
// gcSpool stuff is for debug purposes only
gcSpoolMsgCurrent--;
break; } }
UNLOCKSPOOLER; }
/******************************Public*Routine******************************\
* BOOL AddMessage2Q (PSPOOLMSG psm) * * History: * 6-Aug-1995 -by- Steve Wilson [swilson] * \**************************************************************************/
BOOL AddMessage2Q(PSPOOLMSG psm, DWORD dwSpoolInstance) { BOOL bRet = FALSE;
// add the message to the queue
LOCKSPOOLER;
if (psm == NULL) { bRet = FALSE; } else if ((dwSpoolInstance != gdwSpoolInstance) && (dwSpoolInstance != NON_SPOOL_INSTANCE)) { EngSetLastError(ERROR_INVALID_HANDLE); bRet = FALSE; } else { if (gpsmSpoolTail) { // list isn't empty, so add it to the list
ASSERTGDI(gpsmSpoolHead != NULL,"GreEscapeSpool - head is null\n");
gpsmSpoolTail->pNext = psm; } else { ASSERTGDI(gpsmSpoolHead == NULL,"GreEscapeSpool - head not null\n"); gpsmSpoolHead = psm; }
// the tail now always points to the new element
gpsmSpoolTail = psm; psm->pNext = NULL;
// and some stats
gcSpoolMsg++; gcSpoolMsgCurrent++; if (gcSpoolMsgCurrent > gcSpoolMsgMax) gcSpoolMsgMax = gcSpoolMsgCurrent;
bRet = TRUE; }
UNLOCKSPOOLER;
// notify the spooler that there is a message ready
if (bRet == TRUE) { KeSetEvent(gpeventGdiSpool,0,FALSE); }
return bRet; }
/******************************Public*Routine******************************\
* GreOpenPrinterW() * * History: * 28-Mar-1995 -by- Eric Kutter [erick] * Wrote it. \**************************************************************************/
BOOL WINAPI GreOpenPrinterW( GREOPENPRINTER *pOpenPrinter, LPHANDLE phPrinter) { ULONG bRet; ULONG ul;
SPOOLMEMOBJ spmo; // Creates (PSPOOLOBJ) spmo.pso, containing pkevent, hSpool, pvMsg
//DbgPrint("Enter GreOpenPrinterW\n");
if (spmo.bValid()) { // setup the message
PSPOOLMSG psm = spmo.psm();
psm->cj = sizeof(SPOOLMSG); psm->iMsg = GDISPOOL_OPENPRINTER; psm->fl = 0; psm->cBuf = 1; psm->cjIn = pOpenPrinter->cj; psm->acjIn[0] = pOpenPrinter->cj; psm->apulIn[0]= (PULONG)pOpenPrinter; psm->pulOut = (PULONG)&spmo.pso->hSpool;
psm->cjOut = sizeof(*phPrinter);
psm->hso = spmo.hGet(); // returns ((HSPOOLOBJ) pso)
if (spmo.GreEscapeSpool() && psm->ulRet) { *phPrinter = (HANDLE)spmo.hGet();
bRet = TRUE; } else { spmo.bDelete(); bRet = FALSE; } } else { bRet = FALSE; }
return(bRet); }
/*****************************************************************************
* GreEnumFormsW * * History: * 25/7/95 by Steve Wilson [swilson] * Wrote it. *******************************************************************************/
BOOL WINAPI GreEnumFormsW( HANDLE hSpool, GREENUMFORMS *pEnumForms, GREENUMFORMS *pEnumFormsReturn, LONG cjOut ) { ULONG bRet = FALSE; SPOOLREF sr(hSpool);
if (sr.bValid()) { PSPOOLMSG psm = sr.psm();
//DbgPrint("Enter GreEnumFormsW\n");
// setup the message
psm->cj = sizeof(SPOOLMSG); psm->iMsg = GDISPOOL_ENUMFORMS; psm->fl = 0; psm->cBuf = 1; psm->cjIn = pEnumForms->cj; psm->acjIn[0] = pEnumForms->cj; psm->apulIn[0]= (PULONG) pEnumForms; psm->pulOut = (PULONG) pEnumFormsReturn; psm->cjOut = cjOut; psm->hso = (HSPOOLOBJ) hSpool;
bRet = sr.GreEscapeSpool() ? psm->ulRet : FALSE; }
return bRet; }
/*****************************************************************************
* GreGenericW * * History: * 25-Jul-95 by Steve Wilson [swilson] * Wrote it. *******************************************************************************/
BOOL GreGenericW( HANDLE hSpool, PULONG pX, PULONG pXReturn, LONG cjOut, LONG MessageID, ULONG ulFlag ) { ULONG bRet = FALSE; SPOOLREF sr(hSpool);
if (sr.bValid()) { PSPOOLMSG psm = sr.psm();
//DbgPrint("Enter GreGenericW\n");
// setup the message
psm->cj = sizeof(SPOOLMSG); psm->iMsg = MessageID; psm->fl = ulFlag; psm->cBuf = 1; psm->cjIn = pX ? *(PULONG) pX : 0; // Must be sure first element of pX is LONG cj: (pX->cj)
psm->acjIn[0] = pX ? *(PULONG) pX : 0; // Must be sure first element of pX is LONG cj: (pX->cj)
psm->apulIn[0]= (PULONG) pX; psm->pulOut = (PULONG) pXReturn; psm->cjOut = cjOut; psm->hso = (HSPOOLOBJ) hSpool;
bRet = sr.GreEscapeSpool() ? psm->ulRet : FALSE; }
return bRet; }
/*****************************************************************************\
* GrePrinterDriverUnloadW * * This function is used to a send a message to the spooler when a printer * driver is unloaded (i.e the DC count on the driver goes to zero) * On receipt of this message, the spooler attempts to upgrade the driver if * neccesary. * * History: * 11/17/97 by Ramanathan N Venkatapathy * Wrote it. \*****************************************************************************/
BOOL GrePrinterDriverUnloadW( LPWSTR pDriverName ) { BOOL bRet = FALSE; ULONG cbDriverName; LPWSTR pDriverFile = NULL;
SPOOLMEMOBJ spmo;
// Check for invalid printer driver names.
if (!pDriverName || !*pDriverName) { return bRet; }
// Copy the driver name into another buffer.
cbDriverName = (wcslen(pDriverName) + 1) * sizeof(WCHAR); pDriverFile = (LPWSTR) PALLOCMEM(cbDriverName, 'lpsG');
if (spmo.bValid() && pDriverFile) { PSPOOLMSG psm = spmo.psm();
memcpy(pDriverFile, pDriverName, cbDriverName);
psm->cj = sizeof(SPOOLMSG); psm->iMsg = GDISPOOL_UNLOADDRIVER_COMPLETE; psm->fl = 0;
psm->cBuf = 1; psm->cjIn = cbDriverName; psm->apulIn[0] = (PULONG) pDriverFile; psm->acjIn[0] = cbDriverName;
psm->pulOut = NULL; psm->cjOut = 0;
psm->hso = spmo.hGet();
bRet = spmo.GreEscapeSpool() && psm->ulRet;
spmo.bDelete(); }
if (pDriverFile) { VFREEMEM(pDriverFile); }
return bRet; }
/*****************************************************************************
* GreGetPrinterDriverW * * History: * 4/14/1995 by Gerrit van Wingerden [gerritv] * Wrote it. *******************************************************************************/
BOOL WINAPI GreGetPrinterDriverW( HANDLE hSpool, GREGETPRINTERDRIVER *pGetPrinterDriver, GREGETPRINTERDRIVER *pGetPrinterDriverReturn, LONG cjOut ) { ULONG bRet = FALSE; SPOOLREF sr(hSpool);
if (sr.bValid()) { PSPOOLMSG psm = sr.psm();
//DbgPrint("Enter GreGetPrinterDriverW\n");
// setup the message
psm->cj = sizeof(SPOOLMSG); psm->iMsg = GDISPOOL_GETPRINTERDRIVER; psm->fl = 0; psm->cBuf = 1; psm->cjIn = pGetPrinterDriver->cj; psm->acjIn[0] = pGetPrinterDriver->cj; psm->apulIn[0]= (PULONG) pGetPrinterDriver; psm->pulOut = (PULONG)pGetPrinterDriverReturn; psm->cjOut = cjOut; psm->hso = (HSPOOLOBJ)hSpool;
if( sr.GreEscapeSpool() ) { bRet = psm->ulRet; } else { bRet = FALSE; } }
return(bRet); }
/****************************************************************************
* BOOL StartPagePrinter( HANDLE hPrinter ) * * History: * 4/28/1995 by Gerrit van Wingerden [gerritv] * Wrote it. *****************************************************************************/
BOOL WINAPI StartPagePrinter( HANDLE hSpool ) { BOOL bRet = FALSE; SPOOLREF sr(hSpool);
if (sr.bValid()) { PSPOOLMSG psm = sr.psm();
//DbgPrint("Enter StartPagePrinter\n");
psm->cj = sizeof(SPOOLMSG); psm->iMsg = GDISPOOL_STARTPAGEPRINTER; psm->fl = 0; psm->cBuf = 0; psm->cjIn = 0; psm->pulOut = NULL; psm->cjOut = 0; psm->hso = (HSPOOLOBJ)hSpool;
if( sr.GreEscapeSpool() ) { bRet = psm->ulRet; } }
return(bRet); }
/****************************************************************************
* BOOL EndPagePrinter( HANDLE hPrinter ) * * History: * 4/28/1995 by Gerrit van Wingerden [gerritv] * Wrote it. *****************************************************************************/
BOOL WINAPI EndPagePrinter( HANDLE hSpool ) { BOOL bRet = FALSE; SPOOLREF sr(hSpool);
if (sr.bValid()) { PSPOOLMSG psm = sr.psm();
//DbgPrint("Enter EndPagePrinter\n");
psm->cj = sizeof(SPOOLMSG); psm->iMsg = GDISPOOL_ENDPAGEPRINTER; psm->fl = 0; psm->cBuf = 0; psm->cjIn = 0; psm->pulOut = NULL; psm->cjOut = 0; psm->hso = (HSPOOLOBJ)hSpool;
if(sr.GreEscapeSpool()) { bRet = psm->ulRet; } }
return(bRet);
}
/****************************************************************************
* BOOL EndDocPrinter( HANDLE hPrinter ) * * History: * 4/28/1995 by Gerrit van Wingerden [gerritv] * Wrote it. *****************************************************************************/
BOOL WINAPI EndDocPrinter( HANDLE hSpool ) { BOOL bRet = FALSE; SPOOLREF sr(hSpool);
if (sr.bValid()) { PSPOOLMSG psm = sr.psm();
//DbgPrint("Enter EndDocPrinter\n");
psm->cj = sizeof(SPOOLMSG); psm->iMsg = GDISPOOL_ENDDOCPRINTER; psm->fl = 0; psm->cBuf = 0; psm->cjIn = 0; psm->pulOut = NULL; psm->cjOut = 0; psm->hso = (HSPOOLOBJ)hSpool;
if( sr.GreEscapeSpool() ) { bRet = (DWORD) psm->ulRet; } }
return(bRet);
}
/****************************************************************************
* ClosePrinter( HANDLE hPrinter ) * * History: * 12-Feb-1996 -by- Steve Wilson (swilson) * Wrote it. *****************************************************************************/
BOOL WINAPI ClosePrinter( HANDLE hSpool ) { SPOOLREF sr(hSpool);
if (sr.bValid()) { //DbgPrint("Enter ClosePrinter: %d\n", sr.pso->hSpool);
SendSimpleMessage(sr.pso->hSpool, GDISPOOL_CLOSEPRINTER, sr.pso->dwSpoolInstance);
sr.bDelete(); }
return TRUE; }
/******************************Public*Routine******************************\
* AbortPrinter() * * History: * 18-Jul-1995 -by- Steve Wilson (swilson) * Wrote it. \**************************************************************************/
BOOL WINAPI AbortPrinter( HANDLE hPrinter) { BOOL bRet = FALSE; SPOOLREF sr(hPrinter);
if (sr.bValid()) { PSPOOLMSG psm = sr.psm();
//DbgPrint("Enter AbortPrinter\n");
psm->cj = sizeof(SPOOLMSG); psm->iMsg = GDISPOOL_ABORTPRINTER; psm->fl = 0; psm->cBuf = 0; psm->cjIn = 0; psm->pulOut = NULL; psm->cjOut = 0; psm->hso = (HSPOOLOBJ)hPrinter;
if( sr.GreEscapeSpool() ) { bRet = (DWORD) psm->ulRet; } }
return(bRet); }
/****************************************************************************
* StartDocPrinter() * * History: * 4/28/1995 by Gerrit van Wingerden [gerritv] * Wrote it. *****************************************************************************/
DWORD WINAPI StartDocPrinterW( HANDLE hPrinter, DWORD dwLevel, LPBYTE lpbDocInfo ) { LONG cj = offsetof(GRESTARTDOCPRINTER,alData); LONG cjDocName = 0; LONG cjOutputFile = 0; LONG cjDatatype = 0; DOC_INFO_1W* pDocInfo = (DOC_INFO_1W*) lpbDocInfo; DWORD ret = 0;
//DbgPrint("Enter StartDocPrinterW\n");
ASSERTGDI( dwLevel == 1, "StartDocPrinter: dwLevel != 1\n" ); ASSERTGDI( lpbDocInfo != NULL, "StarDocPrinter lpbDocInfo is NULL\n");
// first we need to compute the sizes.
if (pDocInfo->pDocName) { cjDocName = (wcslen(pDocInfo->pDocName) + 1) * sizeof(WCHAR);
cj += cjDocName; cj = (cj + 3) & ~3; }
if (pDocInfo->pOutputFile) { cjOutputFile = (wcslen(pDocInfo->pOutputFile) + 1) * sizeof(WCHAR);
cj += cjOutputFile; cj = (cj + 3) & ~3; }
if (pDocInfo->pDatatype) { cjDatatype = (wcslen(pDocInfo->pDatatype) + 1) * sizeof(WCHAR);
cj += cjDatatype; cj = (cj + 3) & ~3; }
SPOOLREF sr(hPrinter);
if (sr.bValid()) { PLONG plData;
GRESTARTDOCPRINTER *pStartDocPrinter; pStartDocPrinter = (GRESTARTDOCPRINTER*)PALLOCNOZ(cj,'lpsG');
if (pStartDocPrinter) { PSPOOLMSG psm = sr.psm();
// we got memory, now copy the stuff in
pStartDocPrinter->cj = cj; plData = pStartDocPrinter->alData;
pStartDocPrinter->cjDocName = (cjDocName + 3) & ~3; pStartDocPrinter->cjOutputFile = (cjOutputFile + 3) & ~3; pStartDocPrinter->cjDatatype = (cjDatatype + 3) & ~3;
if (pDocInfo->pDocName) { memcpy(plData,pDocInfo->pDocName,cjDocName); plData += (cjDocName+3)/4; }
if (pDocInfo->pOutputFile) { memcpy(plData,pDocInfo->pOutputFile,cjOutputFile); plData += (cjOutputFile+3)/4; }
if (pDocInfo->pDatatype) { memcpy(plData,pDocInfo->pDatatype,cjDatatype); plData += (cjDatatype+3)/4; }
ASSERTGDI(POFFSET(pStartDocPrinter,plData) == cj, "EngStartDocPrinter - sizes are wrong\n");
// pStartDocPrinter now contains all needed data, call out
// setup the message
psm->cj = sizeof(SPOOLMSG); psm->iMsg = GDISPOOL_STARTDOCPRINTER; psm->fl = 0; psm->cBuf = 1; psm->cjIn = pStartDocPrinter->cj; psm->acjIn[0] = pStartDocPrinter->cj; psm->apulIn[0]= (PULONG)pStartDocPrinter; psm->pulOut = NULL; psm->cjOut = 0; psm->hso = (HSPOOLOBJ)hPrinter;
if( sr.GreEscapeSpool() ) { ret = (DWORD) psm->ulRet; }
VFREEMEM(pStartDocPrinter); } }
return(ret);
}
/******************************Public*Routine******************************\
* OpenPrinterW() * * History: * 05-Apr-1995 -by- Eric Kutter [erick] * Wrote it. \**************************************************************************/
BOOL WINAPI OpenPrinterW( LPWSTR pPrinterName, LPHANDLE phPrinter, LPPRINTER_DEFAULTSW pDefault) { LONG cjName = 0; LONG cjDatatype = 0; LONG cjDevMode = 0; LONG cj = offsetof(GREOPENPRINTER,alData); BOOL bRet = FALSE;
PLONG plData;
GREOPENPRINTER *pOpenPrinter;
//DbgPrint("Enter OpenPrinterW\n");
// first we need to compute the sizes.
if (pPrinterName) { cjName = (wcslen(pPrinterName) + 1) * sizeof(WCHAR);
cj += cjName; cj = (cj + 3) & ~3; }
if (pDefault) { if (pDefault->pDatatype) { cjDatatype = (wcslen(pDefault->pDatatype) + 1) * sizeof(WCHAR);
cj += cjDatatype; cj = (cj + 3) & ~3; }
if (pDefault->pDevMode) {
//DbgPrint("cjMinDevmode = %d, dmSize = %d, dmDriverExtra = %d\n", MIN_DEVMODE_SIZE, pDefault->pDevMode->dmSize, pDefault->pDevMode->dmDriverExtra);
cjDevMode = pDefault->pDevMode->dmSize + pDefault->pDevMode->dmDriverExtra;
if (cjDevMode < MIN_DEVMODE_SIZE) { EngSetLastError(ERROR_INVALID_PARAMETER); return bRet; }
cj += cjDevMode; cj = (cj + 3) & ~3; }
}
// allocate the memory
pOpenPrinter = (GREOPENPRINTER*)PALLOCNOZ(cj,'lpsG');
if (pOpenPrinter) { // we got memory, now copy the stuff in
pOpenPrinter->cj = cj; pOpenPrinter->pd = *pDefault; plData = pOpenPrinter->alData;
pOpenPrinter->cjName = (cjName + 3) & ~3; pOpenPrinter->cjDatatype = (cjDatatype + 3) & ~3; pOpenPrinter->cjDevMode = (cjDevMode + 3) & ~3;
if (pPrinterName) { memcpy(plData,pPrinterName,cjName); plData += (cjName+3)/4; }
if (pDefault) { if (pDefault->pDatatype) { pOpenPrinter->pd.pDatatype = (WCHAR*)POFFSET(pOpenPrinter,plData); memcpy(plData,pDefault->pDatatype,cjDatatype); plData += (cjDatatype+3)/4; }
if (pDefault->pDevMode) { pOpenPrinter->pd.pDevMode = (PDEVMODEW)POFFSET(pOpenPrinter,plData); memcpy(plData,pDefault->pDevMode,cjDevMode); plData += (cjDevMode+3)/4; } }
ASSERTGDI(POFFSET(pOpenPrinter,plData) == cj, "EngOpenPrinter - sizes are wrong\n");
// pOpenPrinter now contains all needed data, call out
bRet = GreOpenPrinterW(pOpenPrinter,phPrinter);
VFREEMEM(pOpenPrinter); }
return(bRet); }
/*******************************************************************************
* void MarshallUpStructure( LPBYTE lpStructure, LPDWORD lpOffsets ) * * This routine does pointer adjustment to offsets within the buffer * * History: * 6/30/1995 by Muhunthan Sivapragasam (MuhuntS) * Got from spoolss code *******************************************************************************/
void MarshallUpStructure( LPBYTE lpStructure, LPDWORD lpOffsets ) { register DWORD i=0;
while (lpOffsets[i] != -1) {
if ((*(LPBYTE *)(lpStructure+lpOffsets[i]))) { (*(LPBYTE *)(lpStructure+lpOffsets[i]))+=(ULONG_PTR)lpStructure; }
i++; } }
/*******************************************************************************
* BOOL ValidateString( LPWSTR pString, PBYTE pBuffer, LONG cjLength ) * * This routine validates a LPWSTR to make sure that it really lies inside of a buffer. * * History: * 4/19/1995 by Gerrit van Wingerden [gerritv] * Wrote it. *******************************************************************************/
BOOL ValidateString( LPWSTR pString, PBYTE pBuffer, LONG cjLength ) { LPWSTR pEnd = (LPWSTR) ( pBuffer + cjLength );
if( pString > pEnd || pString < (LPWSTR) pBuffer ) { return(FALSE); }
while( pString < pEnd && *pString) { pString++; }
return( pString < pEnd );
}
/*******************************************************************************
* BOOL ValidateStrings( LPBYTE lpStructure, LPDWORD lpOffsets, LONG cjLength ) * * This routine validates all the strings in the structure to make sure they really lie inside of a buffer. * * History: * 6/30/1995 by Muhunthan Sivapragasam (MuhuntS) * Wrote it. *******************************************************************************/ BOOL ValidateStrings( LPBYTE lpStructure, LPDWORD lpOffsets, LONG cjLength ) { register DWORD i=0;
while (lpOffsets[i] != -1) {
if ( (*(LPWSTR *) (lpStructure+lpOffsets[i])) && !ValidateString(*(LPWSTR *) (lpStructure+lpOffsets[i]), lpStructure, cjLength) ) { return FALSE; }
i++; }
return TRUE; }
/*******************************************************************************
* BOOL ValidateDependentFiles( LPWSTR pString, PBYTE pBuffer, LONG cjLength ) * * This routine validates DependentFiles field (which is a list of strings * up to \0\0) to make sure that it really lies inside of a buffer. * * History: * 6/30/1995 by Muhunthan Sivapragasam * Wrote it. *******************************************************************************/
BOOL ValidateDependentFiles( LPWSTR pString, PBYTE pBuffer, LONG cjLength ) { LPWSTR pEnd = (LPWSTR) (pBuffer + cjLength) - 1;
if (pString < (LPWSTR) pBuffer) { return(FALSE); }
while (pString < pEnd) { if (*pString==0 && *(pString+1)==0) return TRUE; pString++; }
return (FALSE); }
/*******************************************************************************
* EngEnumForms() * * History: * 7/24/1995 by Steve Wilson [swilson] * Wrote it. *******************************************************************************/
BOOL WINAPI EngEnumForms( HANDLE hPrinter, DWORD dwLevel, LPBYTE lpbForms, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcbReturned ) { LONG cj; DWORD cjReturn; BOOL bRet = FALSE; GREENUMFORMS *pEnumForms, *pEnumFormsReturn;
//DbgPrint("Enter EngEnumFormsW\n");
if(!pcbNeeded || !pcbReturned) { EngSetLastError(ERROR_INVALID_PARAMETER); return FALSE; }
cj = sizeof(GREENUMFORMS);
// Allocate TO buffer
pEnumForms = (GREENUMFORMS *) PALLOCMEM(cj, 'lpsG');
// Marshall TO buffer
if (pEnumForms) {
pEnumForms->cj = cj; pEnumForms->cjData = cbBuf; pEnumForms->dwLevel = dwLevel;
// Allocate RETURN buffer
pEnumFormsReturn = (GREENUMFORMS *) PALLOCNOZ(cjReturn = sizeof(GREENUMFORMS) + cbBuf, 'lpsG');
// SEND MESSAGE
if (pEnumFormsReturn) { bRet = GreEnumFormsW(hPrinter, pEnumForms, pEnumFormsReturn, cjReturn);
// Fill in return sizes
*pcbNeeded = pEnumFormsReturn->cjData; // # return data bytes
*pcbReturned = pEnumFormsReturn->nForms; // # forms in return data
// UnMarshall Message
if (bRet) {
ASSERTGDI(*pcbNeeded <= cbBuf,"EnumFormsW - error\n");
if (lpbForms && *pcbNeeded <= cbBuf) { // Copy returned data into supplied FORM_INFO_1 structure
memcpy(lpbForms, pEnumFormsReturn->alData, pEnumFormsReturn->cjData);
DWORD i;
for (i = 0 ; i < *pcbReturned ; ++i, lpbForms += sizeof(FORM_INFO_1W)) { MarshallUpStructure(lpbForms, FormInfo1Offsets); } } } VFREEMEM(pEnumFormsReturn); } VFREEMEM(pEnumForms); } return bRet; }
/*******************************************************************************
* EngGetPrinter() * * History: * 9/30/1995 by Steve Wilson [swilson] * Wrote it. *******************************************************************************/ BOOL WINAPI EngGetPrinter( HANDLE hPrinter, DWORD dwLevel, LPBYTE pPrinter, DWORD cbBuf, LPDWORD pcbNeeded ) { LONG cj; DWORD cjReturn; BOOL bRet = FALSE; GREGETPRINTER *pGetPrinter, *pGetPrinterReturn;
DWORD *pOffsets;
//DbgPrint("Enter EngGetPrinter\n");
if (!pcbNeeded) { EngSetLastError(ERROR_INVALID_PARAMETER); return FALSE; }
switch (dwLevel) { case 1: pOffsets = PrinterInfo1Offsets; break;
case 2: pOffsets = PrinterInfo2Offsets; break;
case 3: pOffsets = PrinterInfo3Offsets; break;
case 4: pOffsets = PrinterInfo4Offsets; break;
case 5: pOffsets = PrinterInfo5Offsets; break;
default: EngSetLastError(ERROR_INVALID_LEVEL); return FALSE; }
cj = sizeof(GREGETPRINTER);
// Allocate TO buffer
pGetPrinter = (GREGETPRINTER *) PALLOCMEM(cj, 'lpsG');
// Marshall TO buffer
if (pGetPrinter) {
pGetPrinter->cj = cj; pGetPrinter->cjData = cbBuf; pGetPrinter->dwLevel = dwLevel;
// Allocate RETURN buffer
pGetPrinterReturn = (GREGETPRINTER *) PALLOCNOZ(cjReturn = sizeof(GREGETPRINTER) + cbBuf, 'lpsG');
// SEND MESSAGE
if (pGetPrinterReturn) { bRet = GreGenericW(hPrinter, (PULONG) pGetPrinter, (PULONG) pGetPrinterReturn, cjReturn, GDISPOOL_GETPRINTER, 0);
// Fill in return size
*pcbNeeded = pGetPrinterReturn->cjData;
// UnMarshall Message
if (bRet) { ASSERTGDI(*pcbNeeded <= cbBuf,"EngGetPrinter - error\n");
if (pPrinter && *pcbNeeded <= cbBuf) { memcpy(pPrinter, pGetPrinterReturn->alData, pGetPrinterReturn->cjData); MarshallUpStructure(pPrinter, pOffsets); } } VFREEMEM(pGetPrinterReturn); } VFREEMEM(pGetPrinter); } return bRet; }
/*******************************************************************************
* EngGetForm() * * History: * 7/24/1995 by Steve Wilson [swilson] * Wrote it. *******************************************************************************/
BOOL WINAPI EngGetForm( HANDLE hPrinter, LPWSTR pFormName, DWORD dwLevel, LPBYTE lpbForm, DWORD cbBuf, LPDWORD pcbNeeded ) { LONG cj, cjFormName; DWORD cjReturn; BOOL bRet = FALSE; GREGETFORM *pGetForm, *pGetFormReturn;
//DbgPrint("Enter EngGetForm\n");
if (!pcbNeeded) { EngSetLastError(ERROR_INVALID_PARAMETER); return FALSE; }
// Accumulate sizes of base struct plus strings
cj = sizeof(GREGETFORM); cj += (cjFormName = pFormName ? (wcslen(pFormName) + 1)*sizeof *pFormName : 0);
// Allocate TO buffer
pGetForm = (GREGETFORM *) PALLOCMEM(cj, 'lpsG');
// Marshall TO buffer
if (pGetForm) { pGetForm->cj = cj; pGetForm->cjData = cbBuf; pGetForm->dwLevel = dwLevel;
if (pFormName) { memcpy(pGetForm->alData,pFormName,cjFormName); }
// Allocate RETURN buffer
pGetFormReturn = (GREGETFORM *) PALLOCNOZ(cjReturn = sizeof(GREGETFORM) + cbBuf, 'lpsG');
// SEND MESSAGE
if (pGetFormReturn) { bRet = GreGenericW(hPrinter, (PULONG) pGetForm, (PULONG) pGetFormReturn, cjReturn, GDISPOOL_GETFORM, 0);
if (bRet) {
// Fill in return sizes
*pcbNeeded = pGetFormReturn->cjData; // # return data bytes
// UnMarshall Message
if (lpbForm && bRet) {
if (*pcbNeeded <= cbBuf) memcpy(lpbForm, pGetFormReturn->alData, pGetFormReturn->cjData); else ASSERTGDI(*pcbNeeded <= cbBuf,"GetFormW - error\n");
MarshallUpStructure(lpbForm, FormInfo1Offsets);
} } VFREEMEM(pGetFormReturn); } VFREEMEM(pGetForm); } return bRet;
}
/*******************************************************************************
* GetPrinterDriverW() * * History: * 4/19/1995 by Gerrit van Wingerden [gerritv] * Wrote it. *******************************************************************************/
BOOL WINAPI GetPrinterDriverW( HANDLE hPrinter, LPWSTR pEnvironment, DWORD dwLevel, LPBYTE lpbDrvInfo, DWORD cbBuf, LPDWORD pcbNeeded ) { BOOL bRet = FALSE; LONG cj, cjEnvironment; DWORD *pOffsets, *pStrings;
//DbgPrint("Enter GetPrinterDriverW\n");
if (!pcbNeeded) { EngSetLastError(ERROR_INVALID_PARAMETER); return FALSE; }
cj = sizeof(GREGETPRINTERDRIVER); cjEnvironment = 0;
*pcbNeeded = 0;
if( pEnvironment ) { cjEnvironment += (wcslen(pEnvironment) + 1) * sizeof(WCHAR); }
cj += cjEnvironment;
GREGETPRINTERDRIVER *pGetPrinterDriver;
pGetPrinterDriver = (GREGETPRINTERDRIVER*)PALLOCMEM(cj, 'lpsG');
if( pGetPrinterDriver ) { pGetPrinterDriver->cj = cj; pGetPrinterDriver->cjData = cbBuf; pGetPrinterDriver->dwLevel = dwLevel;
if( pEnvironment ) { memcpy(pGetPrinterDriver->alData,pEnvironment,cjEnvironment); }
GREGETPRINTERDRIVER *pGetPrinterDriverReturn = NULL; UINT cjSize = cbBuf + sizeof(GREGETPRINTERDRIVER);
pGetPrinterDriverReturn = (GREGETPRINTERDRIVER*) PALLOCNOZ(cjSize, 'lpsG');
if( pGetPrinterDriverReturn ) { bRet = GreGetPrinterDriverW(hPrinter,pGetPrinterDriver, pGetPrinterDriverReturn, cjSize );
DWORD cjData = pGetPrinterDriverReturn->cjData;
if (bRet == FALSE) { if (cjData > cbBuf) { // need to return the size needed.
*pcbNeeded = pGetPrinterDriverReturn->cjData; } } else { ASSERTGDI(cjData <= cbBuf,"GetPrinterDriverW - error\n");
// return the size needed whether everything fits or not
*pcbNeeded = cjData;
// only copy data and return success if everything fits
switch (dwLevel) { case 1: pOffsets = DriverInfo1Offsets; pStrings = DriverInfo1Strings; break;
case 2: pOffsets = DriverInfo2Offsets; pStrings = DriverInfo2Strings; break;
case 3: pOffsets = DriverInfo3Offsets; pStrings = DriverInfo3Strings; break;
default: // We should not get here
ASSERTGDI(0, "GetPrinterDriverW invalid level\n"); }
if (lpbDrvInfo) { memcpy( lpbDrvInfo, pGetPrinterDriverReturn->alData, cjData ); MarshallUpStructure((LPBYTE)lpbDrvInfo, pOffsets); if ( !ValidateStrings((LPBYTE)lpbDrvInfo, pStrings, cjData) || (dwLevel == 3 && ((PDRIVER_INFO_3W) lpbDrvInfo)->pDependentFiles && !ValidateDependentFiles(((PDRIVER_INFO_3W) lpbDrvInfo)->pDependentFiles, (LPBYTE)lpbDrvInfo, cjData) ) ) { WARNING("GetPrinterDriverW: String does not fit in buffer\n"); bRet = FALSE; } } }
VFREEMEM(pGetPrinterDriverReturn); }
VFREEMEM(pGetPrinterDriver); } return(bRet); }
/******************************Public*Routine******************************\
* * Routine Name: * * EngGetPrinterDriver * * Routine Description: * * Arguments: * * Called by: * * Return Value: * \**************************************************************************/
extern "C" BOOL APIENTRY EngGetPrinterDriver( HANDLE hPrinter , LPWSTR pEnvironment , DWORD dwLevel , BYTE *lpbDrvInfo , DWORD cbBuf , DWORD *pcbNeeded ) { BOOL ReturnValue; ReturnValue = GetPrinterDriverW( hPrinter , pEnvironment , dwLevel , lpbDrvInfo , cbBuf , pcbNeeded ); return( ReturnValue ); }
/*******************************************************************************
* EngGetPrinterDataW() * * History: * 25-Jul-95 by Steve Wilson [swilson] * Wrote it. *******************************************************************************/
DWORD WINAPI EngGetPrinterData( HANDLE hPrinter, // IN
LPWSTR pValueName, // IN
LPDWORD pType, // OUT -- may be NULL
LPBYTE lpbData, // OUT
DWORD cbBuf, // IN
LPDWORD pcbNeeded // OUT
) { LONG cj, cjValueName; DWORD cjReturn; DWORD dwLastError = ERROR_NOT_ENOUGH_MEMORY; GREGETPRINTERDATA *pX, *pXReturn;
//DbgPrint("Enter EngGetPrinterData\n");
if (!pcbNeeded) { EngSetLastError(ERROR_INVALID_PARAMETER); return ERROR_INVALID_PARAMETER; }
// Accumulate sizes of base struct plus strings
cj = sizeof *pX; cj += (cjValueName = pValueName ? (wcslen(pValueName) + 1)*sizeof *pValueName : 0);
// Allocate TO buffer
pX = (GREGETPRINTERDATA *) PALLOCMEM(cj, 'lpsG');
// Marshall TO buffer
if (pX) {
pX->cj = cj; pX->cjData = cbBuf; // Client's buffer size
// Write strings at end of GRE struct
if (pValueName) { memcpy(pX->alData,pValueName,cjValueName); pX->cjValueName = cjValueName; }
// Allocate RETURN buffer
pXReturn = (GREGETPRINTERDATA *) PALLOCNOZ(cjReturn = sizeof *pX + cbBuf, 'lpsG');
// SEND MESSAGE
if (pXReturn) { // GreGenericW return value indicates success or failure of GreEscapeSpool() and KMGetPrinterData()
GreGenericW( hPrinter, (PULONG) pX, (PULONG) pXReturn, cjReturn, (LONG) GDISPOOL_GETPRINTERDATA, 0);
dwLastError = EngGetLastError();
// return from GreGenericW may be 0, meaning any number of things, including ERROR_MORE_DATA
if (dwLastError != ERROR_PROCESS_ABORTED) { // Fill in return sizes
if (pcbNeeded) { *pcbNeeded = pXReturn->dwNeeded; // # return data bytes
//DbgPrint("EngGetPrinterData *pcbNeeded = %d\n", *pcbNeeded);
}
if (pType) *pType = pXReturn->dwType;
if (dwLastError == ERROR_SUCCESS) { // Copy returned data into supplied structure
if (lpbData) { if ((DWORD) pXReturn->cjData <= cbBuf) memcpy(lpbData, pXReturn->alData, pXReturn->cjData); else ASSERTGDI((DWORD) pXReturn->cjData <= cbBuf, "GetPrinterDataW - Bad spooler buffer size\n"); } } }
VFREEMEM(pXReturn); } VFREEMEM(pX); }
//DbgPrint("GetPrinterData return: %d\n", dwLastError);
return dwLastError; }
/*******************************************************************************
* EngSetPrinterData() * * History: * 25-Oct-95 by Steve Wilson [swilson] * Wrote it. *******************************************************************************/
DWORD WINAPI EngSetPrinterData( HANDLE hPrinter, // IN
LPWSTR pType, // IN
DWORD dwType, // IN
LPBYTE lpbPrinterData, // IN
DWORD cjPrinterData // IN
) { LONG cj, cjType; DWORD cjReturn; DWORD dwLastError = ERROR_NOT_ENOUGH_MEMORY; GRESETPRINTERDATA *pTo, *pFrom;
//DbgPrint("Enter EngSetPrinterData\n");
// Accumulate sizes of base struct plus strings
cj = sizeof *pTo; cj += (cjType = pType ? (wcslen(pType) + 1)*sizeof *pType : 0); cj += lpbPrinterData ? cjPrinterData : 0;
// Allocate TO buffer
pTo = (GRESETPRINTERDATA *) PALLOCMEM(cj, 'lpsG');
// Marshall TO buffer
if (pTo) {
pTo->cj = cj;
// Write incoming data at end of GRE struct
if (pType) { memcpy(pTo->alData,pType,cjType); pTo->cjType = cjType; }
if (lpbPrinterData) { memcpy((BYTE *)pTo->alData + cjType,lpbPrinterData,cjPrinterData); pTo->cjPrinterData = cjPrinterData; }
// Allocate RETURN buffer
cjReturn = sizeof *pTo; pFrom = (GRESETPRINTERDATA *) PALLOCNOZ(cjReturn, 'lpsG');
// SEND MESSAGE
if (pFrom) { pTo->dwType = dwType;
// GreGenericW return value indicates success or failure of GreEscapeSpool() and KMGetPrinterData()
GreGenericW( hPrinter, (PULONG) pTo, (PULONG) pFrom, cjReturn, (LONG) GDISPOOL_SETPRINTERDATA, 0);
dwLastError = EngGetLastError();
VFREEMEM(pFrom); } VFREEMEM(pTo); }
//DbgPrint("GetPrinterData return: %d\n", dwLastError);
return dwLastError; }
/******************************Public*Routine******************************\
* * * History: * 27-Feb-1995 -by- Eric Kutter [erick] * Wrote it. \**************************************************************************/
BOOL WINAPI EngWritePrinter( HANDLE hPrinter, LPVOID pBuf, DWORD cbBuf, LPDWORD pcWritten ) { DWORD ulRet = 0; SPOOLREF sr(hPrinter);
if (sr.bValid()) { PSPOOLMSG psm = sr.psm(); LPVOID pKmBuf = NULL;
GREWRITEPRINTER *wp = &sr.pso->WritePrinter;
//DbgPrint("Enter EngWritePrinter\n");
wp->cj = offsetof(GREWRITEPRINTER,alData) + cbBuf; wp->cjData = cbBuf;
if (gpeSpool == PsGetCurrentProcess() && pBuf <= MM_HIGHEST_USER_ADDRESS && pBuf >= MM_LOWEST_USER_ADDRESS) { wp->pUserModeData = (PULONG) pBuf; wp->cjUserModeData = cbBuf; psm->cBuf = 1; psm->cjIn = offsetof(GREWRITEPRINTER, alData); } else { //
// if we recevie a user mode buffer, make a kernel copy.
// This is to patch PSCRIPT 4 driver running on NT5.
//
if (pBuf <= MM_HIGHEST_USER_ADDRESS && pBuf >= MM_LOWEST_USER_ADDRESS) { if (pKmBuf = PALLOCNOZ(cbBuf,'lpsG')) { __try { ProbeAndReadBuffer(pKmBuf,pBuf,cbBuf); } __except(EXCEPTION_EXECUTE_HANDLER) { VFREEMEM(pKmBuf); return (FALSE); }
pBuf = pKmBuf; } else { WARNING ("failed to allocate memory in EngWritePrinter\n"); return (FALSE); } }
wp->pUserModeData = NULL; wp->cjUserModeData = 0; psm->cBuf = 2; psm->apulIn[1]= (PULONG)pBuf; psm->acjIn[1] = cbBuf; psm->cjIn = wp->cj; }
// setup the message
psm->cj = sizeof(SPOOLMSG); psm->iMsg = GDISPOOL_WRITE; psm->fl = 0;
// there are two buffers, one for the GREWRITEPRINTER header and one
// for the data.
psm->apulIn[0]= (PULONG)wp; psm->acjIn[0] = offsetof(GREWRITEPRINTER,alData);
// place the return value in ulRet
psm->pulOut = &sr.pso->dwWritePrinterReturn; psm->cjOut = sizeof(sr.pso->dwWritePrinterReturn); psm->hso = (HSPOOLOBJ)hPrinter;
if (!sr.GreEscapeSpool()) { ulRet = 0; } else { ulRet = sr.pso->dwWritePrinterReturn; }
if (pcWritten) *pcWritten = ulRet;
if (pKmBuf) { VFREEMEM(pKmBuf); } }
// return TRUE or FALSE
return(!!ulRet); }
/*******************************************************************************
* BOOL GetFontPathName( WCHAR *pFullPath, WCHAR *pFileName ) * * Goes to the spooler and does a search path in the font path to find the * full path given a font file name. We expect pFullName and pFileName to * point to the same piece of memory although we don't require this to be the case. * * History * 7-31-95 Gerrit van Wingerden [gerritv] * Wrote it. * *******************************************************************************/
BOOL GetFontPathName( WCHAR *pFullPath, WCHAR *pFileName ) { BOOL bRet = FALSE; SPOOLMEMOBJ spmo;
if (spmo.bValid()) { PSPOOLMSG psm = spmo.psm();
psm->cj = sizeof(SPOOLMSG); psm->iMsg = GDISPOOL_GETPATHNAME; psm->fl = 0;
psm->cBuf = 1; psm->cjIn = (wcslen(pFileName) + 1) * sizeof(WCHAR); psm->apulIn[0] = (PULONG) pFileName; psm->acjIn[0] = psm->cjIn;
psm->pulOut = (PULONG) pFullPath; psm->cjOut = sizeof(WCHAR) * (MAX_PATH+1);
psm->hso = spmo.hGet();
bRet = spmo.GreEscapeSpool() && psm->ulRet;
spmo.bDelete(); }
return(bRet);
}
/******************************Public*Routine******************************\
* EngEscape() * * History: * 18-Sep-1995 -by- Eric Kutter [erick] * Wrote it. \**************************************************************************/
ULONG APIENTRY EngEscape( HANDLE hPrinter, ULONG iEsc, ULONG cjIn, PVOID pvIn, ULONG cjOut, PVOID pvOut) { BOOL ulRet = FALSE; SPOOLREF sr(hPrinter);
if (sr.bValid()) { PSPOOLMSG psm = sr.psm();
psm->cj = sizeof(SPOOLMSG); psm->iMsg = GDISPOOL_ENDDOCPRINTER; psm->fl = 0;
psm->cBuf = 2; psm->cjIn = sizeof(ULONG)+cjIn;
psm->apulIn[0]= &iEsc; psm->acjIn[0] = sizeof(ULONG);
psm->apulIn[1]= (PULONG)pvIn; psm->acjIn[1] = cjIn;
psm->pulOut = (PULONG)pvOut; psm->cjOut = cjOut;
psm->hso = (HSPOOLOBJ)hPrinter;
if( sr.GreEscapeSpool() ) { ulRet = (DWORD) psm->ulRet; } }
return(ulRet); }
|