/******************************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); }