Leaked source code of windows server 2003
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.

426 lines
11 KiB

  1. #include <IrpQueue.h>
  2. #include <winerror.h> // For S_OK, S_FALSE, and E_UNEXPECTED
  3. #pragma optimize("w",off)
  4. #pragma optimize("a",off)
  5. const int CGuardedIrpQueue::CANCEL_IRPS_ON_DELETE = 0x00000001;
  6. const int CGuardedIrpQueue::PRESERVE_QUEUE_ORDER = 0x00000002;
  7. const int CGuardedIrpQueue::LIFO_QUEUE_ORDER = 0x00000004;
  8. PIRP CTempIrpQueue::Remove()
  9. {
  10. PIRP pIrp = NULL;
  11. if(!IsListEmpty(&m_QueueHead))
  12. {
  13. PLIST_ENTRY pListEntry;
  14. if(m_fLIFO)
  15. {
  16. pListEntry = RemoveTailList(&m_QueueHead);
  17. }
  18. else
  19. {
  20. pListEntry = RemoveHeadList(&m_QueueHead);
  21. }
  22. // Get the IRP from the ListEntry in the IRP
  23. pIrp = (PIRP)CONTAINING_RECORD(pListEntry, IRP, Tail.Overlay.ListEntry);
  24. }
  25. return pIrp;
  26. }
  27. void _stdcall DriverCancel(PDEVICE_OBJECT, PIRP pIrp)
  28. {
  29. CGuardedIrpQueue *pGuardedIrpQueue;
  30. pGuardedIrpQueue = reinterpret_cast<CGuardedIrpQueue *>(pIrp->Tail.Overlay.DriverContext[3]);
  31. pGuardedIrpQueue->CancelIrp(pIrp);
  32. }
  33. void CGuardedIrpQueue::Init(int iFlags, PFN_DEC_IRP_COUNT pfnDecIrpCount, PVOID pvContext)
  34. {
  35. m_iFlags = iFlags;
  36. m_pfnDecIrpCount = pfnDecIrpCount;
  37. m_pvContext = pvContext;
  38. InitializeListHead(&m_QueueHead);
  39. KeInitializeSpinLock(&m_QueueLock);
  40. }
  41. void CGuardedIrpQueue::Destroy(NTSTATUS NtStatus)
  42. {
  43. if(m_iFlags & CANCEL_IRPS_ON_DELETE)
  44. {
  45. CancelAll(NtStatus);
  46. }
  47. ASSERT(IsListEmpty(&m_QueueHead));
  48. }
  49. NTSTATUS CGuardedIrpQueue::Add(PIRP pIrp)
  50. {
  51. KIRQL OldIrql;
  52. KeAcquireSpinLock(&m_QueueLock, &OldIrql);
  53. return AddImpl(pIrp, OldIrql);
  54. }
  55. PIRP CGuardedIrpQueue::Remove()
  56. {
  57. KIRQL OldIrql;
  58. KeAcquireSpinLock(&m_QueueLock, &OldIrql);
  59. PIRP pIrp = RemoveImpl();
  60. KeReleaseSpinLock(&m_QueueLock, OldIrql);
  61. return pIrp;
  62. }
  63. PIRP CGuardedIrpQueue::RemoveByPointer(PIRP pIrp)
  64. {
  65. KIRQL OldIrql;
  66. KeAcquireSpinLock(&m_QueueLock, &OldIrql);
  67. pIrp = RemoveByPointerImpl(pIrp);
  68. KeReleaseSpinLock(&m_QueueLock, OldIrql);
  69. return pIrp;
  70. }
  71. ULONG CGuardedIrpQueue::RemoveByFileObject(PFILE_OBJECT pFileObject, CTempIrpQueue *pTempIrpQueue)
  72. {
  73. KIRQL OldIrql;
  74. KeAcquireSpinLock(&m_QueueLock, &OldIrql);
  75. ULONG ulReturn = RemoveByFileObjectImpl(pFileObject, pTempIrpQueue);
  76. KeReleaseSpinLock(&m_QueueLock, OldIrql);
  77. return ulReturn;
  78. }
  79. ULONG CGuardedIrpQueue::RemoveAll(CTempIrpQueue *pTempIrpQueue)
  80. {
  81. KIRQL OldIrql;
  82. KeAcquireSpinLock(&m_QueueLock, &OldIrql);
  83. ULONG ulReturn = RemoveAllImpl(pTempIrpQueue);
  84. KeReleaseSpinLock(&m_QueueLock, OldIrql);
  85. return ulReturn;
  86. }
  87. NTSTATUS CGuardedIrpQueue::AddImpl(PIRP pIrp, KIRQL OldIrql)
  88. {
  89. BOOLEAN fCancelHere = FALSE;
  90. // Mark incoming IRP pending
  91. IoMarkIrpPending(pIrp);
  92. // mark IRP in DriverContext so that we can find this instance of the queue
  93. // in the cancel routine
  94. pIrp->Tail.Overlay.DriverContext[3] = reinterpret_cast<PVOID>(this);
  95. // Set our cancel routine
  96. IoSetCancelRoutine(pIrp, DriverCancel);
  97. //If the IRP was cancelled before it got to us, don't queue it, mark
  98. //it to cancel after we release the lock (a few lines down)
  99. if(pIrp->Cancel)
  100. {
  101. IoSetCancelRoutine(pIrp, NULL);
  102. fCancelHere = TRUE;
  103. }
  104. else
  105. //Queue IRP unless it was marked to cancel
  106. {
  107. // Insert Item in Queue (items always added at the tail)
  108. InsertTailList(&m_QueueHead, &pIrp->Tail.Overlay.ListEntry);
  109. }
  110. //Release spin lock
  111. KeReleaseSpinLock(&m_QueueLock, OldIrql);
  112. //If it had been marked for cancel, do it here
  113. if(fCancelHere)
  114. {
  115. pIrp->IoStatus.Information = 0;
  116. pIrp->IoStatus.Status = STATUS_CANCELLED;
  117. IoCompleteRequest(pIrp, IO_NO_INCREMENT);
  118. m_pfnDecIrpCount(m_pvContext);
  119. return STATUS_CANCELLED;
  120. }
  121. // return Pending as we have queued the IRP
  122. return STATUS_PENDING;
  123. }
  124. PIRP CGuardedIrpQueue::RemoveImpl()
  125. {
  126. KIRQL OldIrql;
  127. PIRP pReturnIrp = NULL;
  128. PLIST_ENTRY pListEntry;
  129. // Skip getting the IRP and all, if queue is empty
  130. if(!IsListEmpty(&m_QueueHead))
  131. {
  132. //Remove head or tail depending on LIFO or FIFO (we always add to the tail)
  133. if(m_iFlags & LIFO_QUEUE_ORDER)
  134. {
  135. pListEntry = RemoveTailList(&m_QueueHead);
  136. }
  137. else
  138. {
  139. pListEntry = RemoveHeadList(&m_QueueHead);
  140. }
  141. // Get the IRP from the ListEntry in the IRP
  142. pReturnIrp = (PIRP)CONTAINING_RECORD(pListEntry, IRP, Tail.Overlay.ListEntry);
  143. // Unset the cancel routine
  144. IoSetCancelRoutine(pReturnIrp, NULL);
  145. }
  146. // Return the IRP, or NULL if there weren't any
  147. return pReturnIrp;
  148. }
  149. PIRP CGuardedIrpQueue::RemoveByPointerImpl(PIRP pIrp)
  150. {
  151. PIRP pFoundIrp = NULL;
  152. PIRP pCurrentIrp;
  153. PLIST_ENTRY pCurrentListEntry;
  154. PLIST_ENTRY pQueueFirstItem = NULL;
  155. // Pop IRPs off the queue and put them back until we find it
  156. if( !IsListEmpty(&m_QueueHead) )
  157. {
  158. pCurrentListEntry = RemoveHeadList(&m_QueueHead);
  159. pQueueFirstItem = pCurrentListEntry;
  160. do{
  161. //Get the IRP from the entry
  162. pCurrentIrp = CONTAINING_RECORD(pCurrentListEntry, IRP, Tail.Overlay.ListEntry);
  163. //Check for match
  164. if(pCurrentIrp == pIrp)
  165. {
  166. ASSERT(!pFoundIrp); //serious error, means IRP was in queue twice
  167. pFoundIrp = pCurrentIrp;
  168. //clear the cancel routine (do it here, as we still have the spin lock)
  169. IoSetCancelRoutine(pFoundIrp, NULL);
  170. // If we need to preserve the queue order,
  171. // keep removing and adding until we are through the list once
  172. if( m_iFlags & PRESERVE_QUEUE_ORDER )
  173. {
  174. //If the list is now empty we are done
  175. if(IsListEmpty(&m_QueueHead))
  176. {
  177. break;
  178. }
  179. // The found entry is not going, back in the list
  180. // so if it was first, it no longer is.
  181. if(pQueueFirstItem == pCurrentListEntry)
  182. {
  183. pQueueFirstItem = NULL;
  184. }
  185. //Get the next IRP
  186. pCurrentListEntry = RemoveHeadList(&m_QueueHead);
  187. pCurrentIrp = CONTAINING_RECORD(pCurrentListEntry, IRP, Tail.Overlay.ListEntry);
  188. ASSERT(pFoundIrp != pCurrentIrp); //serious error, means IRP was in queue twice
  189. //If the first item is NULL (four line up), this new entry is it
  190. if(!pQueueFirstItem)
  191. {
  192. pQueueFirstItem = pCurrentListEntry;
  193. }
  194. }
  195. //If the order does not need to be preserved, we are done
  196. else
  197. {
  198. break;
  199. }
  200. }
  201. //This next item cannot be a match, if it was we
  202. //have moved on to the next one already
  203. // Put the IRP back in the queue
  204. InsertTailList(&m_QueueHead, pCurrentListEntry);
  205. // Get the next item (no need to check if list is empty,
  206. // we just put an item in
  207. pCurrentListEntry = RemoveHeadList(&m_QueueHead);
  208. //check if done
  209. if (pCurrentListEntry == pQueueFirstItem)
  210. {
  211. //put it back, if we are done.
  212. InsertHeadList(&m_QueueHead, pCurrentListEntry);
  213. //Mark as NULL, so that we do not iterate again
  214. pCurrentListEntry = NULL;
  215. }
  216. } while (pCurrentListEntry);
  217. }
  218. //Return the IRP we found, or NULL if it was not in the Queue
  219. return pFoundIrp;
  220. }
  221. ULONG CGuardedIrpQueue::RemoveByFileObjectImpl(PFILE_OBJECT pFileObject, CTempIrpQueue *pTempIrpQueue)
  222. {
  223. PIRP pCurrentIrp;
  224. PIO_STACK_LOCATION pIrpStack;
  225. PLIST_ENTRY pCurrentListEntry;
  226. PLIST_ENTRY pQueueFirstItem = NULL;
  227. PLIST_ENTRY pTempQueueListEntry;
  228. ULONG ulMatchCount=0;
  229. //Get the list entry from the temp queue
  230. pTempQueueListEntry = &pTempIrpQueue->m_QueueHead;
  231. pTempIrpQueue->m_fLIFO = m_iFlags & LIFO_QUEUE_ORDER;
  232. // Pop IRPs off the queue and put them back until we find it
  233. if( !IsListEmpty(&m_QueueHead) )
  234. {
  235. pCurrentListEntry = RemoveHeadList(&m_QueueHead);
  236. pQueueFirstItem = pCurrentListEntry;
  237. do{
  238. //Get the IRP from the entry
  239. pCurrentIrp = CONTAINING_RECORD(pCurrentListEntry, IRP, Tail.Overlay.ListEntry);
  240. //Get the Stack Location
  241. pIrpStack = IoGetCurrentIrpStackLocation(pCurrentIrp);
  242. //Check for matching file object
  243. if(pIrpStack->FileObject == pFileObject)
  244. {
  245. //Increment match count
  246. ulMatchCount++;
  247. //clear the cancel routine
  248. IoSetCancelRoutine(pCurrentIrp, NULL);
  249. //Move it over to the simple queue
  250. InsertTailList(pTempQueueListEntry, pCurrentListEntry);
  251. //If the list is empty we are done
  252. if( IsListEmpty(&m_QueueHead) )
  253. {
  254. break;
  255. }
  256. //If it was the first item in the list, it is no longer
  257. if(pQueueFirstItem == pCurrentListEntry)
  258. {
  259. pQueueFirstItem = NULL;
  260. }
  261. //setup for next iteration
  262. pCurrentListEntry = RemoveHeadList(&m_QueueHead);
  263. //If it was the first item in the list, it is no longer
  264. if(!pQueueFirstItem)
  265. {
  266. pQueueFirstItem = pCurrentListEntry;
  267. }
  268. }
  269. else
  270. {
  271. // Put the IRP back in the queue
  272. InsertTailList(&m_QueueHead, pCurrentListEntry);
  273. // Get the next item (no need to check if list is empty,
  274. // we just put an item in)
  275. pCurrentListEntry = RemoveHeadList(&m_QueueHead);
  276. //check if done
  277. if (pCurrentListEntry == pQueueFirstItem)
  278. {
  279. //put it back, if we are done.
  280. InsertHeadList(&m_QueueHead, pCurrentListEntry);
  281. //Mark as NULL, so that we do not iterate again
  282. pCurrentListEntry = NULL;
  283. }
  284. }
  285. } while (pCurrentListEntry);
  286. }
  287. //Return the IRP we found, or NULL if it was not in the Queue
  288. return ulMatchCount;
  289. }
  290. ULONG CGuardedIrpQueue::RemoveAllImpl(CTempIrpQueue *pTempIrpQueue)
  291. {
  292. PLIST_ENTRY pCurrentListEntry;
  293. PIRP pCurrentIrp;
  294. PLIST_ENTRY pTempQueueListEntry;
  295. ULONG ulCount=0;
  296. //Get a pointer to the simple queue's list entry
  297. pTempQueueListEntry = &pTempIrpQueue->m_QueueHead;
  298. pTempIrpQueue->m_fLIFO = m_iFlags & LIFO_QUEUE_ORDER;
  299. //Move all the items
  300. while(!IsListEmpty(&m_QueueHead))
  301. {
  302. ulCount++;
  303. //Get next IRP
  304. pCurrentListEntry = RemoveHeadList(&m_QueueHead);
  305. pCurrentIrp = CONTAINING_RECORD(pCurrentListEntry, IRP, Tail.Overlay.ListEntry);
  306. //Clear the cancel routine
  307. IoSetCancelRoutine(pCurrentIrp, NULL);
  308. //Move to other list
  309. InsertTailList(pTempQueueListEntry, pCurrentListEntry);
  310. }
  311. //return count
  312. return ulCount;
  313. }
  314. void CGuardedIrpQueue::CancelIrp(PIRP pIrp)
  315. {
  316. PIRP pFoundIrp = RemoveByPointer(pIrp);
  317. //Release the cancel lock
  318. IoReleaseCancelSpinLock(pIrp->CancelIrql);
  319. //If the IRP was found cancel it and decrement IRP count
  320. if(pFoundIrp)
  321. {
  322. pFoundIrp->IoStatus.Information = 0;
  323. pFoundIrp->IoStatus.Status = STATUS_CANCELLED;
  324. IoCompleteRequest(pFoundIrp, IO_NO_INCREMENT);
  325. m_pfnDecIrpCount(m_pvContext);
  326. }
  327. }
  328. void CGuardedIrpQueue::CancelByFileObject(PFILE_OBJECT pFileObject)
  329. {
  330. CTempIrpQueue TempIrpQueue;
  331. PIRP pFoundIrp;
  332. //Get all the IRP's to cancel
  333. RemoveByFileObject(pFileObject, &TempIrpQueue);
  334. //If the IRP was found cancel it and decrement IRP count
  335. while(pFoundIrp = TempIrpQueue.Remove())
  336. {
  337. pFoundIrp->IoStatus.Information = 0;
  338. pFoundIrp->IoStatus.Status = STATUS_CANCELLED;
  339. IoCompleteRequest(pFoundIrp, IO_NO_INCREMENT);
  340. m_pfnDecIrpCount(m_pvContext);
  341. }
  342. }
  343. void CGuardedIrpQueue::CancelAll(NTSTATUS NtStatus)
  344. {
  345. CTempIrpQueue TempIrpQueue;
  346. PIRP pFoundIrp;
  347. //Get all the IRP's to cancel
  348. RemoveAll(&TempIrpQueue);
  349. //If the IRP was found cancel it and decrement IRP count
  350. while(pFoundIrp = TempIrpQueue.Remove())
  351. {
  352. pFoundIrp->IoStatus.Information = 0;
  353. pFoundIrp->IoStatus.Status = NtStatus;
  354. IoCompleteRequest(pFoundIrp, IO_NO_INCREMENT);
  355. m_pfnDecIrpCount(m_pvContext);
  356. }
  357. }