Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

911 lines
19 KiB

  1. /*++
  2. Copyright (c) 1997 Microsoft Corporation
  3. All rights reserved
  4. Module Name:
  5. handle.c
  6. Abstract:
  7. Contains all functions related to the maintanence of print handles.
  8. Author:
  9. Environment:
  10. User Mode -Win32
  11. Revision History:
  12. --*/
  13. #include "precomp.h"
  14. #pragma hdrstop
  15. #include "client.h"
  16. DWORD
  17. OpenPrinterRPC(
  18. PSPOOL pSpool
  19. );
  20. BOOL
  21. ClosePrinterRPC(
  22. IN PSPOOL pSpool,
  23. IN BOOL bRevalidate
  24. );
  25. BOOL
  26. ClosePrinterContextHandle(
  27. HANDLE hPrinter
  28. );
  29. BOOL
  30. ClosePrinterWorker(
  31. PSPOOL pSpool
  32. );
  33. DWORD gcClientHandle = 0;
  34. #ifdef DBG_TRACE_HANDLE
  35. PSPOOL gpFirstSpool = NULL;
  36. #endif
  37. EProtectResult
  38. eProtectHandle(
  39. IN HANDLE hPrinter,
  40. IN BOOL bClose
  41. )
  42. /*++
  43. Routine Description:
  44. Protect a print handle so that it will not be deleted while it is
  45. being used. If this is called by the Close routine, then this call
  46. returns whether the Close should continue or be aborted.
  47. Note: This only provides close protection--it does not guard against
  48. simultaneous access by non-close operations.
  49. There must always be a matching vUnprotect call when the callee is
  50. done with the handle.
  51. Arguments:
  52. hPrinter - pSpool to protect.
  53. bClose - If TRUE, indicates that the callee wants to close the handle.
  54. (Generally called by ClosePrinter only.) The return value will
  55. indicate whether the calleeis allowed to close the printer.
  56. Return Value:
  57. kProtectHandleSuccess - Call succeeded; printer handle can be used normally.
  58. kProtectHandleInvalid - Handle is invalid; call failed.
  59. kProtectHandlePendingDeletion - This only occurs when bClose is TRUE. The
  60. Operation on handle is in process, and the close will happen when the
  61. other thread has completed.
  62. LastError only set if handle kProtectHandleInvalid is returned.
  63. --*/
  64. {
  65. EProtectResult eResult = kProtectHandleInvalid;
  66. PSPOOL pSpool = (PSPOOL)hPrinter;
  67. vEnterSem();
  68. try {
  69. if( pSpool &&
  70. (pSpool->signature == SP_SIGNATURE ) &&
  71. !( pSpool->Status & ( SPOOL_STATUS_CLOSE |
  72. SPOOL_STATUS_PENDING_DELETION ))){
  73. //
  74. // Valid handle.
  75. //
  76. eResult = kProtectHandleSuccess;
  77. } else {
  78. DBGMSG( DBG_WARN,
  79. ( "Bad hPrinter %x %x\n",
  80. pSpool,
  81. pSpool ? pSpool->signature : 0 ));
  82. }
  83. } except( EXCEPTION_EXECUTE_HANDLER ){
  84. DBGMSG( DBG_WARN, ( "Unmapped pSpool %x\n", pSpool ));
  85. }
  86. if( eResult == kProtectHandleSuccess ){
  87. //
  88. // If bClose, then see if an operation is currently executing.
  89. //
  90. if( bClose ){
  91. if(( pSpool->Status & SPOOL_STATUS_PENDING_DELETION ) ||
  92. pSpool->cActive ){
  93. //
  94. // Mark pSpool to close itself once the operation has
  95. // completed in the other thread.
  96. //
  97. pSpool->Status |= SPOOL_STATUS_PENDING_DELETION;
  98. eResult = kProtectHandlePendingDeletion;
  99. } else {
  100. //
  101. // No call is active, so mark ourselves as closing so
  102. // that no other call will succeed using this handle.
  103. //
  104. pSpool->Status |= SPOOL_STATUS_CLOSE;
  105. }
  106. }
  107. } else {
  108. //
  109. // Not a valid handle.
  110. //
  111. SetLastError( ERROR_INVALID_HANDLE );
  112. }
  113. if( eResult == kProtectHandleSuccess ){
  114. //
  115. // Returning success, we are now active.
  116. //
  117. ++pSpool->cActive;
  118. }
  119. vLeaveSem();
  120. return eResult;
  121. }
  122. VOID
  123. vUnprotectHandle(
  124. IN HANDLE hPrinter
  125. )
  126. /*++
  127. Routine Description:
  128. Unprotect a print handle. This must be called once for each
  129. successful bProtectHandle.
  130. Arguments:
  131. hPrinter - Handle to unprotect.
  132. Return Value:
  133. --*/
  134. {
  135. PSPOOL pSpool = (PSPOOL)hPrinter;
  136. BOOL bCallClosePrinter = FALSE;
  137. vEnterSem();
  138. //
  139. // No longer active. However, it it's closing, leave it marked
  140. // as closing since we don't want anyone else to use it.
  141. //
  142. --pSpool->cActive;
  143. if( pSpool->Status & SPOOL_STATUS_PENDING_DELETION &&
  144. !pSpool->cActive ){
  145. //
  146. // Someone called Close while we were active. Since we are now
  147. // going to close it, don't let anyone else initiate a close by
  148. // marking SPOOL_STATUS_CLOSE.
  149. //
  150. pSpool->Status |= SPOOL_STATUS_CLOSE;
  151. pSpool->Status &= ~SPOOL_STATUS_PENDING_DELETION;
  152. bCallClosePrinter = TRUE;
  153. }
  154. vLeaveSem();
  155. if( bCallClosePrinter ){
  156. ClosePrinterWorker( pSpool );
  157. }
  158. }
  159. /********************************************************************
  160. OpenPrinter worker functions.
  161. ********************************************************************/
  162. BOOL
  163. OpenPrinterW(
  164. LPWSTR pPrinterName,
  165. LPHANDLE phPrinter,
  166. LPPRINTER_DEFAULTS pDefault
  167. )
  168. {
  169. HANDLE hPrinter;
  170. PSPOOL pSpool = NULL;
  171. DWORD dwError;
  172. //
  173. // Pre-initialize the out parameter, so that *phPrinter is NULL
  174. // on failure. This fixes Borland Paradox 7.
  175. //
  176. try {
  177. *phPrinter = NULL;
  178. } except( EXCEPTION_EXECUTE_HANDLER ){
  179. SetLastError(TranslateExceptionCode(GetExceptionCode()));
  180. return FALSE;
  181. }
  182. pSpool = AllocSpool();
  183. if( !pSpool ){
  184. goto Fail;
  185. }
  186. //
  187. // Copy DevMode, defaults. The printer name doesn't change.
  188. //
  189. if( !UpdatePrinterDefaults( pSpool, pPrinterName, pDefault )){
  190. goto Fail;
  191. }
  192. //
  193. // Update the access, since this is not set by UpdatePrinterDefaults.
  194. //
  195. if( pDefault ){
  196. pSpool->Default.DesiredAccess = pDefault->DesiredAccess;
  197. }
  198. dwError = OpenPrinterRPC( pSpool );
  199. if( dwError != ERROR_SUCCESS ){
  200. SetLastError( dwError );
  201. goto Fail;
  202. }
  203. //
  204. // We finally have a good pSpool. Only now update the output
  205. // handle. Since it was NULL initialized, this guarantees that
  206. // OpenPrinter returns *phPrinter NULL when it fails.
  207. //
  208. *phPrinter = pSpool;
  209. return TRUE;
  210. Fail:
  211. FreeSpool( pSpool );
  212. return FALSE;
  213. }
  214. DWORD
  215. OpenPrinterRPC(
  216. PSPOOL pSpool
  217. )
  218. /*++
  219. Routine Description:
  220. Open the printer handle using information in the pSpool object.
  221. Arguments:
  222. pSpool - Printer handle to open. Internal state of pSpool updated.
  223. Return Value:
  224. ERROR_SUCCES - Succeed.
  225. Status code - Failed.
  226. --*/
  227. {
  228. DEVMODE_CONTAINER DevModeContainer;
  229. HANDLE hPrinter = NULL;
  230. DWORD dwReturn;
  231. DWORD dwSize;
  232. SPLCLIENT_CONTAINER SplClientContainer;
  233. DevModeContainer.cbBuf = 0;
  234. DevModeContainer.pDevMode = NULL;
  235. SplClientContainer.Level = 2;
  236. SplClientContainer.ClientInfo.pClientInfo2 = NULL;
  237. RpcTryExcept {
  238. //
  239. // Construct the DevMode container.
  240. //
  241. if( bValidDevModeW( pSpool->Default.pDevMode )){
  242. dwSize = pSpool->Default.pDevMode->dmSize +
  243. pSpool->Default.pDevMode->dmDriverExtra;
  244. DevModeContainer.cbBuf = pSpool->Default.pDevMode->dmSize +
  245. pSpool->Default.pDevMode->dmDriverExtra;
  246. DevModeContainer.pDevMode = (LPBYTE)pSpool->Default.pDevMode;
  247. }
  248. //
  249. // If the call is made from within the spooler, we also retrieve the
  250. // server side hPrinter. This will help avoid unnecessary RPC. We cant,
  251. // however, avoid RPC in this case since the spooler may need a client side
  252. // handle to pass to other functions or the driver.
  253. //
  254. if (bLoadedBySpooler) {
  255. if (SplClientContainer.ClientInfo.pClientInfo2 =
  256. (LPSPLCLIENT_INFO_2) AllocSplMem(sizeof(SPLCLIENT_INFO_2))) {
  257. SplClientContainer.ClientInfo.pClientInfo2->hSplPrinter = 0;
  258. dwReturn = RpcSplOpenPrinter( (LPTSTR)pSpool->pszPrinter,
  259. &hPrinter,
  260. pSpool->Default.pDatatype,
  261. &DevModeContainer,
  262. pSpool->Default.DesiredAccess,
  263. &SplClientContainer );
  264. } else {
  265. SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  266. dwReturn = ERROR_NOT_ENOUGH_MEMORY;
  267. }
  268. } else {
  269. dwReturn = RpcOpenPrinter( (LPTSTR)pSpool->pszPrinter,
  270. &hPrinter,
  271. pSpool->Default.pDatatype,
  272. &DevModeContainer,
  273. pSpool->Default.DesiredAccess );
  274. }
  275. } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
  276. dwReturn = TranslateExceptionCode( RpcExceptionCode() );
  277. } RpcEndExcept
  278. if( dwReturn == ERROR_SUCCESS ){
  279. vEnterSem();
  280. //
  281. // hPrinter gets adopted by pSpool->hPrinter.
  282. //
  283. pSpool->hPrinter = hPrinter;
  284. if (bLoadedBySpooler) {
  285. pSpool->hSplPrinter = (HANDLE) SplClientContainer.ClientInfo.pClientInfo2->hSplPrinter;
  286. } else {
  287. pSpool->hSplPrinter = NULL;
  288. }
  289. vLeaveSem();
  290. }
  291. if (SplClientContainer.ClientInfo.pClientInfo2) {
  292. FreeSplMem(SplClientContainer.ClientInfo.pClientInfo2);
  293. }
  294. return dwReturn;
  295. }
  296. /********************************************************************
  297. ClosePrinter worker functions.
  298. ********************************************************************/
  299. BOOL
  300. ClosePrinter(
  301. HANDLE hPrinter
  302. )
  303. {
  304. PSPOOL pSpool = (PSPOOL)hPrinter;
  305. switch( eProtectHandle( hPrinter, TRUE )){
  306. case kProtectHandleInvalid:
  307. return FALSE;
  308. case kProtectHandlePendingDeletion:
  309. return TRUE;
  310. default:
  311. break;
  312. }
  313. //
  314. // Note, there isn't a corresponding vUnprotectHandle, but that's ok
  315. // since we're deleting the handle.
  316. //
  317. return ClosePrinterWorker( pSpool );
  318. }
  319. //
  320. // A simpler way to have a central function for closing spool file handles so we
  321. // don't have to reproduce code constantly.
  322. //
  323. VOID
  324. CloseSpoolFileHandles(
  325. PSPOOL pSpool
  326. )
  327. {
  328. if ( pSpool->hSpoolFile != INVALID_HANDLE_VALUE )
  329. {
  330. CloseHandle( pSpool->hSpoolFile );
  331. pSpool->hSpoolFile = INVALID_HANDLE_VALUE;
  332. }
  333. if (pSpool->hFile != INVALID_HANDLE_VALUE)
  334. {
  335. CloseHandle(pSpool->hFile);
  336. pSpool->hFile = INVALID_HANDLE_VALUE;
  337. }
  338. }
  339. BOOL
  340. ClosePrinterWorker(
  341. PSPOOL pSpool
  342. )
  343. {
  344. BOOL bReturnValue;
  345. FlushBuffer(pSpool, NULL);
  346. if (pSpool->Status & SPOOL_STATUS_ADDJOB)
  347. ScheduleJobWorker( pSpool, pSpool->JobId );
  348. vEnterSem();
  349. if( pSpool->pNotify ){
  350. //
  351. // There is a notification; disassociate it from
  352. // pSpool, since we are about to free it.
  353. //
  354. pSpool->pNotify->pSpool = NULL;
  355. }
  356. vLeaveSem();
  357. //
  358. // Close any open file handles, we do this before the RPC closeprinter
  359. // to allow the closeprinter on the other side a chance to delete the spool
  360. // files if they still exist.
  361. //
  362. CloseSpoolFileHandles( pSpool );
  363. bReturnValue = ClosePrinterRPC( pSpool, FALSE );
  364. FreeSpool( pSpool );
  365. return bReturnValue;
  366. }
  367. BOOL
  368. ClosePrinterRPC(
  369. IN PSPOOL pSpool,
  370. IN BOOL bRevalidate
  371. )
  372. /*++
  373. Routine Description:
  374. Close down all RPC/network handles related to the pSpool object.
  375. Must be called outside the critical section. This function also
  376. is called handle revalidation in which case we don't want to
  377. close the event handle on the client side.
  378. Arguments:
  379. pSpool - Spooler handle to shut down.
  380. bRevalidate - If TRUE, this is being called as a result of a handle
  381. revalidation.
  382. Return Value:
  383. TRUE - Success
  384. FALSE - Failed, LastError set.
  385. --*/
  386. {
  387. BOOL bRetval = FALSE;
  388. HANDLE hPrinterRPC = NULL;
  389. vEnterSem();
  390. hPrinterRPC = pSpool->hPrinter;
  391. if ( hPrinterRPC )
  392. {
  393. pSpool->hPrinter = NULL;
  394. FindClosePrinterChangeNotificationWorker( pSpool->pNotify,
  395. hPrinterRPC,
  396. bRevalidate );
  397. vLeaveSem();
  398. bRetval = ClosePrinterContextHandle( hPrinterRPC );
  399. }
  400. else
  401. {
  402. vLeaveSem();
  403. SetLastError( ERROR_INVALID_HANDLE );
  404. }
  405. return bRetval;
  406. }
  407. BOOL
  408. ClosePrinterContextHandle(
  409. HANDLE hPrinterRPC
  410. )
  411. /*++
  412. Routine Description:
  413. Close a printer context handle.
  414. Arguments:
  415. hPrinterRPC - RPC context handle to close.
  416. Return Value:
  417. TRUE - Success
  418. FALSE - Failure; LastError set
  419. --*/
  420. {
  421. BOOL bReturnValue;
  422. DWORD Status;
  423. if( !hPrinterRPC ){
  424. return FALSE;
  425. }
  426. RpcTryExcept {
  427. if( Status = RpcClosePrinter( &hPrinterRPC )) {
  428. SetLastError( Status );
  429. bReturnValue = FALSE;
  430. } else {
  431. bReturnValue = TRUE;
  432. }
  433. } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) {
  434. SetLastError(TranslateExceptionCode(RpcExceptionCode()));
  435. bReturnValue = FALSE;
  436. } RpcEndExcept
  437. //
  438. // If we failed for some reason, then RpcClosePrinter did not
  439. // zero out the context handle. Destroy it here.
  440. //
  441. if( hPrinterRPC ){
  442. RpcSmDestroyClientContext( &hPrinterRPC );
  443. }
  444. return bReturnValue;
  445. }
  446. /********************************************************************
  447. Constructor and destructor of pSpool.
  448. ********************************************************************/
  449. PSPOOL
  450. AllocSpool(
  451. VOID
  452. )
  453. /*++
  454. Routine Description:
  455. Allocate a spool handle. Client should set pSpool->hPrinter
  456. when it is acquired.
  457. Arguments:
  458. Return Value:
  459. pSpool - allocated handle.
  460. NULL - failed.
  461. --*/
  462. {
  463. PSPOOL pSpool = AllocSplMem(sizeof(SPOOL));
  464. if( pSpool ){
  465. InterlockedIncrement( &gcClientHandle );
  466. pSpool->signature = SP_SIGNATURE;
  467. pSpool->hFile = INVALID_HANDLE_VALUE;
  468. pSpool->hSpoolFile = INVALID_HANDLE_VALUE;
  469. #ifdef DBG_TRACE_HANDLE
  470. {
  471. ULONG Hash;
  472. //
  473. // Add to linked list.
  474. //
  475. vEnterSem();
  476. pSpool->pNext = gpFirstSpool;
  477. gpFirstSpool = pSpool;
  478. vLeaveSem();
  479. #if i386
  480. //
  481. // Capture backtrace.
  482. //
  483. RtlCaptureStackBackTrace( 1,
  484. COUNTOF( pSpool->apvBackTrace ),
  485. pSpool->apvBackTrace,
  486. &Hash );
  487. #endif
  488. }
  489. #endif
  490. }
  491. return pSpool;
  492. }
  493. VOID
  494. FreeSpool(
  495. PSPOOL pSpool
  496. )
  497. {
  498. if( !pSpool ){
  499. return;
  500. }
  501. InterlockedDecrement( &gcClientHandle );
  502. if (pSpool->pBuffer != NULL ) {
  503. if (!VirtualFree(pSpool->pBuffer, 0, MEM_RELEASE)) {
  504. DBGMSG(DBG_WARNING, ("ClosePrinter VirtualFree Failed %x\n",
  505. GetLastError()));
  506. }
  507. DBGMSG(DBG_TRACE, ("Closeprinter cWritePrinters %d cFlushBuffers %d\n",
  508. pSpool->cWritePrinters, pSpool->cFlushBuffers));
  509. }
  510. FreeSplStr( pSpool->pszPrinter );
  511. FreeSplMem( pSpool->Default.pDevMode );
  512. FreeSplMem( pSpool->Default.pDatatype );
  513. FreeSplMem( pSpool->pDoceventFilter);
  514. CloseSpoolFileHandles( pSpool );
  515. #ifdef DBG_TRACE_HANDLE
  516. {
  517. //
  518. // Free from linked list.
  519. //
  520. PSPOOL *ppSpool;
  521. vEnterSem();
  522. for( ppSpool = &gpFirstSpool; *ppSpool; ppSpool = &(*ppSpool)->pNext ){
  523. if( *ppSpool == pSpool ){
  524. break;
  525. }
  526. }
  527. if( *ppSpool ){
  528. *ppSpool = pSpool->pNext;
  529. } else {
  530. DBGMSG( DBG_WARN,
  531. ( "pSpool %x not found on linked list\n", pSpool ));
  532. }
  533. vLeaveSem();
  534. }
  535. #endif
  536. FreeSplMem( pSpool );
  537. }
  538. /********************************************************************
  539. Utility functions.
  540. ********************************************************************/
  541. BOOL
  542. RevalidateHandle(
  543. PSPOOL pSpool
  544. )
  545. /*++
  546. Routine Description:
  547. Revalidates a pSpool with a new RPC handle. This allows the spooler
  548. to be restarted yet allow the handle to remain valid.
  549. This should only be called when a call fails with ERROR_INVALID_HANDLE.
  550. We can only save simple state information (pDefaults) from OpenPrinter
  551. and ResetPrinter. If a user spooling and the context handle is lost,
  552. there is no hope of recovering the spool file state, since the
  553. spooler probably died before it could flush its buffers.
  554. We should not encounter any infinite loops when the server goes down,
  555. since the initial call will timeout with an RPC rather than invalid
  556. handle code.
  557. Note: If the printer is renamed, the context handle remains valid,
  558. but revalidation will fail, since we store the old printer name.
  559. Arguments:
  560. pSpool - Printer handle to revalidate.
  561. Return Value:
  562. TRUE - Success
  563. FALSE - Failed.
  564. --*/
  565. {
  566. DWORD dwError;
  567. HANDLE hPrinter;
  568. //
  569. // Close the existing handle. We can't shouldn't just destroy the client
  570. // context since an api may return ERROR_INVALID_HANDLE even though
  571. // RPC context handle is fine (a handle downstream went bad).
  572. //
  573. ClosePrinterRPC( pSpool, TRUE );
  574. //
  575. // Reopen the printer handle with current defaults.
  576. //
  577. dwError = OpenPrinterRPC( pSpool );
  578. if( dwError ){
  579. SetLastError( dwError );
  580. return FALSE;
  581. }
  582. return TRUE;
  583. }
  584. BOOL
  585. UpdatePrinterDefaults(
  586. IN OUT PSPOOL pSpool,
  587. IN LPCTSTR pszPrinter, OPTIONAL
  588. IN PPRINTER_DEFAULTS pDefault OPTIONAL
  589. )
  590. /*++
  591. Routine Description:
  592. Update the pSpool to the new defaults in pDefault, EXCEPT for
  593. pDefault->DesiredAccess.
  594. Since this attempts to read and update pSpool, we enter the
  595. critical section and revalidate the pSpool.
  596. Arguments:
  597. pSpool - Spooler handle to update.
  598. pszPrinter - New printer name.
  599. pDefault - New defaults.
  600. Return Value:
  601. TRUE - Success
  602. FALSE - Failure.
  603. --*/
  604. {
  605. BOOL bReturnValue = FALSE;
  606. vEnterSem();
  607. if( !UpdateString( pszPrinter, &pSpool->pszPrinter )){
  608. goto DoneExitSem;
  609. }
  610. if( pDefault ){
  611. //
  612. // Update the Datatype.
  613. //
  614. if( !UpdateString( pDefault->pDatatype, &pSpool->Default.pDatatype )){
  615. goto DoneExitSem;
  616. }
  617. //
  618. // Update the DevMode.
  619. //
  620. if( bValidDevModeW( pDefault->pDevMode )){
  621. DWORD dwSize;
  622. PDEVMODE pDevModeNew;
  623. dwSize = pDefault->pDevMode->dmSize +
  624. pDefault->pDevMode->dmDriverExtra;
  625. pDevModeNew = AllocSplMem( dwSize );
  626. if( !pDevModeNew ){
  627. goto DoneExitSem;
  628. }
  629. CopyMemory( pDevModeNew, pDefault->pDevMode, dwSize );
  630. FreeSplMem( pSpool->Default.pDevMode );
  631. pSpool->Default.pDevMode = pDevModeNew;
  632. }
  633. }
  634. bReturnValue = TRUE;
  635. DoneExitSem:
  636. vLeaveSem();
  637. return bReturnValue;
  638. }