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.

436 lines
11 KiB

  1. /*++
  2. Copyright (c) 20001 Microsoft Corporation
  3. Module Name:
  4. cmhook.c
  5. Abstract:
  6. Provides routines for implementing callbacks into the registry code.
  7. Callbacks are to be used by the virus filter drivers and cluster
  8. replication engine.
  9. Author:
  10. Dragos C. Sambotin (DragosS) 20-Mar-2001
  11. Revision History:
  12. --*/
  13. #include "cmp.h"
  14. #define CM_MAX_CALLBACKS 100 //TBD
  15. typedef struct _CM_CALLBACK_CONTEXT_BLOCK {
  16. LARGE_INTEGER Cookie; // to identify a specific callback for deregistration purposes
  17. LIST_ENTRY ThreadListHead; // Active threads inside this callback
  18. FAST_MUTEX ThreadListLock; // syncronize access to the above
  19. PVOID CallerContext;
  20. } CM_CALLBACK_CONTEXT_BLOCK, *PCM_CALLBACK_CONTEXT_BLOCK;
  21. typedef struct _CM_ACTIVE_NOTIFY_THREAD {
  22. LIST_ENTRY ThreadList;
  23. PETHREAD Thread;
  24. } CM_ACTIVE_NOTIFY_THREAD, *PCM_ACTIVE_NOTIFY_THREAD;
  25. #ifdef ALLOC_DATA_PRAGMA
  26. #pragma data_seg("PAGEDATA")
  27. #endif
  28. ULONG CmpCallBackCount = 0;
  29. EX_CALLBACK CmpCallBackVector[CM_MAX_CALLBACKS];
  30. VOID
  31. CmpInitCallback(VOID);
  32. BOOLEAN
  33. CmpCheckRecursionAndRecordThreadInfo(
  34. PCM_CALLBACK_CONTEXT_BLOCK CallbackBlock,
  35. PCM_ACTIVE_NOTIFY_THREAD ActiveThreadInfo
  36. );
  37. #ifdef ALLOC_PRAGMA
  38. #pragma alloc_text(PAGE,CmRegisterCallback)
  39. #pragma alloc_text(PAGE,CmUnRegisterCallback)
  40. #pragma alloc_text(PAGE,CmpInitCallback)
  41. #pragma alloc_text(PAGE,CmpCallCallBacks)
  42. #pragma alloc_text(PAGE,CmpCheckRecursionAndRecordThreadInfo)
  43. #endif
  44. NTSTATUS
  45. CmRegisterCallback(IN PEX_CALLBACK_FUNCTION Function,
  46. IN PVOID Context,
  47. IN OUT PLARGE_INTEGER Cookie
  48. )
  49. /*++
  50. Routine Description:
  51. Registers a new callback.
  52. Arguments:
  53. Return Value:
  54. --*/
  55. {
  56. PEX_CALLBACK_ROUTINE_BLOCK RoutineBlock;
  57. ULONG i;
  58. PCM_CALLBACK_CONTEXT_BLOCK CmCallbackContext;
  59. PAGED_CODE();
  60. CmCallbackContext = (PCM_CALLBACK_CONTEXT_BLOCK)ExAllocatePoolWithTag (PagedPool,
  61. sizeof (CM_CALLBACK_CONTEXT_BLOCK),
  62. 'bcMC');
  63. if( CmCallbackContext == NULL ) {
  64. return STATUS_INSUFFICIENT_RESOURCES;
  65. }
  66. RoutineBlock = ExAllocateCallBack (Function,CmCallbackContext);
  67. if( RoutineBlock == NULL ) {
  68. ExFreePool(CmCallbackContext);
  69. return STATUS_INSUFFICIENT_RESOURCES;
  70. }
  71. //
  72. // init the context
  73. //
  74. KeQuerySystemTime(&(CmCallbackContext->Cookie));
  75. *Cookie = CmCallbackContext->Cookie;
  76. InitializeListHead(&(CmCallbackContext->ThreadListHead));
  77. ExInitializeFastMutex(&(CmCallbackContext->ThreadListLock));
  78. CmCallbackContext->CallerContext = Context;
  79. //
  80. // find a spot where we could add this callback
  81. //
  82. for( i=0;i<CM_MAX_CALLBACKS;i++) {
  83. if( ExCompareExchangeCallBack (&CmpCallBackVector[i],RoutineBlock,NULL) ) {
  84. InterlockedExchangeAdd ((PLONG) &CmpCallBackCount, 1);
  85. return STATUS_SUCCESS;
  86. }
  87. }
  88. //
  89. // no more callbacks
  90. //
  91. ExFreePool(CmCallbackContext);
  92. ExFreeCallBack(RoutineBlock);
  93. return STATUS_INSUFFICIENT_RESOURCES;
  94. }
  95. NTSTATUS
  96. CmUnRegisterCallback(IN LARGE_INTEGER Cookie)
  97. /*++
  98. Routine Description:
  99. Unregisters a callback.
  100. Arguments:
  101. Return Value:
  102. --*/
  103. {
  104. ULONG i;
  105. PCM_CALLBACK_CONTEXT_BLOCK CmCallbackContext;
  106. PEX_CALLBACK_ROUTINE_BLOCK RoutineBlock;
  107. NTSTATUS Status;
  108. PAGED_CODE();
  109. //
  110. // Search for this cookie
  111. //
  112. for( i=0;i<CM_MAX_CALLBACKS;i++) {
  113. RoutineBlock = ExReferenceCallBackBlock(&(CmpCallBackVector[i]) );
  114. if( RoutineBlock ) {
  115. CmCallbackContext = (PCM_CALLBACK_CONTEXT_BLOCK)ExGetCallBackBlockContext(RoutineBlock);
  116. if( CmCallbackContext && (CmCallbackContext->Cookie.QuadPart == Cookie.QuadPart) ) {
  117. //
  118. // found it
  119. //
  120. if( ExCompareExchangeCallBack (&CmpCallBackVector[i],NULL,RoutineBlock) ) {
  121. InterlockedExchangeAdd ((PLONG) &CmpCallBackCount, -1);
  122. ExDereferenceCallBackBlock (&(CmpCallBackVector[i]),RoutineBlock);
  123. //
  124. // wait for others to release their reference, then tear down the structure
  125. //
  126. ExWaitForCallBacks (RoutineBlock);
  127. ExFreePool(CmCallbackContext);
  128. ExFreeCallBack(RoutineBlock);
  129. return STATUS_SUCCESS;
  130. }
  131. } else {
  132. ExDereferenceCallBackBlock (&(CmpCallBackVector[i]),RoutineBlock);
  133. }
  134. }
  135. }
  136. return STATUS_INVALID_PARAMETER;
  137. }
  138. NTSTATUS CmpTestCallback(
  139. IN PVOID CallbackContext,
  140. IN PVOID Argument1,
  141. IN PVOID Argument2
  142. );
  143. //
  144. // Cm internals
  145. //
  146. NTSTATUS
  147. CmpCallCallBacks (
  148. IN REG_NOTIFY_CLASS Type,
  149. IN PVOID Argument
  150. )
  151. /*++
  152. Routine Description:
  153. This function calls the callback thats inside a callback structure
  154. Arguments:
  155. Type - Nt call selector
  156. Argument - Caller provided argument to pass on (one of the REG_*_INFORMATION )
  157. Return Value:
  158. NTSTATUS - STATUS_SUCCESS or error status returned by the first callback
  159. --*/
  160. {
  161. NTSTATUS Status;
  162. ULONG i;
  163. PEX_CALLBACK_ROUTINE_BLOCK RoutineBlock;
  164. PCM_CALLBACK_CONTEXT_BLOCK CmCallbackContext;
  165. PAGED_CODE();
  166. for(i=0;i<CM_MAX_CALLBACKS;i++) {
  167. RoutineBlock = ExReferenceCallBackBlock(&(CmpCallBackVector[i]) );
  168. if( RoutineBlock != NULL ) {
  169. //
  170. // we have a safe reference on this block.
  171. //
  172. //
  173. // record thread on a stack struct, so we don't need to allocate pool for it. We unlink
  174. // it from our lists prior to this function exit, so we are on the safe side.
  175. //
  176. CM_ACTIVE_NOTIFY_THREAD ActiveThreadInfo;
  177. //
  178. // get context info
  179. //
  180. CmCallbackContext = (PCM_CALLBACK_CONTEXT_BLOCK)ExGetCallBackBlockContext(RoutineBlock);
  181. ASSERT( CmCallbackContext != NULL );
  182. ActiveThreadInfo.Thread = PsGetCurrentThread();
  183. #if DBG
  184. InitializeListHead(&(ActiveThreadInfo.ThreadList));
  185. #endif //DBG
  186. if( CmpCheckRecursionAndRecordThreadInfo(CmCallbackContext,&ActiveThreadInfo) ) {
  187. Status = ExGetCallBackBlockRoutine(RoutineBlock)(CmCallbackContext->CallerContext,(PVOID)Type,Argument);
  188. //
  189. // now that we're down, remove ourselves from the thread list
  190. //
  191. ExAcquireFastMutex(&(CmCallbackContext->ThreadListLock));
  192. RemoveEntryList(&(ActiveThreadInfo.ThreadList));
  193. ExReleaseFastMutex(&(CmCallbackContext->ThreadListLock));
  194. } else {
  195. ASSERT( IsListEmpty(&(ActiveThreadInfo.ThreadList)) );
  196. }
  197. ExDereferenceCallBackBlock (&(CmpCallBackVector[i]),RoutineBlock);
  198. if( !NT_SUCCESS(Status) ) {
  199. //
  200. // don't bother calling other callbacks if this one vetoed.
  201. //
  202. return Status;
  203. }
  204. }
  205. }
  206. return STATUS_SUCCESS;
  207. }
  208. VOID
  209. CmpInitCallback(VOID)
  210. /*++
  211. Routine Description:
  212. Init the callback module
  213. Arguments:
  214. Return Value:
  215. --*/
  216. {
  217. ULONG i;
  218. PAGED_CODE();
  219. CmpCallBackCount = 0;
  220. for( i=0;i<CM_MAX_CALLBACKS;i++) {
  221. ExInitializeCallBack (&(CmpCallBackVector[i]));
  222. }
  223. /*
  224. {
  225. LARGE_INTEGER Cookie;
  226. if( NT_SUCCESS(CmRegisterCallback(CmpTestCallback,NULL,&Cookie) ) ) {
  227. DbgPrint("Test Hooks installed\n");
  228. }
  229. }
  230. */
  231. }
  232. BOOLEAN
  233. CmpCheckRecursionAndRecordThreadInfo(
  234. PCM_CALLBACK_CONTEXT_BLOCK CallbackBlock,
  235. PCM_ACTIVE_NOTIFY_THREAD ActiveThreadInfo
  236. )
  237. /*++
  238. Routine Description:
  239. Checks if current thread is already inside the callback (recursion avoidance)
  240. Arguments:
  241. Return Value:
  242. --*/
  243. {
  244. PLIST_ENTRY AnchorAddr;
  245. PCM_ACTIVE_NOTIFY_THREAD CurrentThreadInfo;
  246. PAGED_CODE();
  247. ExAcquireFastMutex(&(CallbackBlock->ThreadListLock));
  248. //
  249. // walk the ActiveThreadList and see if we are already active
  250. //
  251. AnchorAddr = &(CallbackBlock->ThreadListHead);
  252. CurrentThreadInfo = (PCM_ACTIVE_NOTIFY_THREAD)(CallbackBlock->ThreadListHead.Flink);
  253. while ( CurrentThreadInfo != (PCM_ACTIVE_NOTIFY_THREAD)AnchorAddr ) {
  254. CurrentThreadInfo = CONTAINING_RECORD(
  255. CurrentThreadInfo,
  256. CM_ACTIVE_NOTIFY_THREAD,
  257. ThreadList
  258. );
  259. if( CurrentThreadInfo->Thread == ActiveThreadInfo->Thread ) {
  260. //
  261. // already there!
  262. //
  263. ExReleaseFastMutex(&(CallbackBlock->ThreadListLock));
  264. return FALSE;
  265. }
  266. //
  267. // skip to the next element
  268. //
  269. CurrentThreadInfo = (PCM_ACTIVE_NOTIFY_THREAD)(CurrentThreadInfo->ThreadList.Flink);
  270. }
  271. //
  272. // add this thread
  273. //
  274. InsertTailList(&(CallbackBlock->ThreadListHead), &(ActiveThreadInfo->ThreadList));
  275. ExReleaseFastMutex(&(CallbackBlock->ThreadListLock));
  276. return TRUE;
  277. }
  278. //
  279. // test hook procedure
  280. //
  281. BOOLEAN CmpCallbackSpew = FALSE;
  282. NTSTATUS CmpTestCallback(
  283. IN PVOID CallbackContext,
  284. IN PVOID Argument1,
  285. IN PVOID Argument2
  286. )
  287. {
  288. REG_NOTIFY_CLASS Type;
  289. PAGED_CODE();
  290. if( !CmpCallbackSpew ) return STATUS_SUCCESS;
  291. Type = (REG_NOTIFY_CLASS)Argument1;
  292. switch( Type ) {
  293. case RegNtDeleteKey:
  294. {
  295. PREG_DELETE_KEY_INFORMATION pDelete = (PREG_DELETE_KEY_INFORMATION)Argument2;
  296. //
  297. // Code to handle NtDeleteKey
  298. //
  299. DbgPrint("Callback(NtDeleteKey) called, arg = %p\n",pDelete);
  300. }
  301. break;
  302. case RegNtSetValueKey:
  303. {
  304. PREG_SET_VALUE_KEY_INFORMATION pSetValue = (PREG_SET_VALUE_KEY_INFORMATION)Argument2;
  305. //
  306. // Code to handle NtSetValueKey
  307. //
  308. DbgPrint("Callback(NtSetValueKey) called, arg = %p\n",pSetValue);
  309. }
  310. break;
  311. case RegNtDeleteValueKey:
  312. {
  313. PREG_DELETE_VALUE_KEY_INFORMATION pDeteteValue = (PREG_DELETE_VALUE_KEY_INFORMATION)Argument2;
  314. //
  315. // Code to handle NtDeleteValueKey
  316. //
  317. DbgPrint("Callback(NtDeleteValueKey) called, arg = %p\n",pDeteteValue);
  318. }
  319. break;
  320. case RegNtSetInformationKey:
  321. {
  322. PREG_SET_INFORMATION_KEY_INFORMATION pSetInfo = (PREG_SET_INFORMATION_KEY_INFORMATION)Argument2;
  323. //
  324. // Code to handle NtSetInformationKey
  325. //
  326. DbgPrint("Callback(NtSetInformationKey) called, arg = %p\n",pSetInfo);
  327. }
  328. break;
  329. default:
  330. DbgPrint("Callback(%lx) called, arg = %p - We don't handle this call\n",(ULONG)Type,Argument2);
  331. break;
  332. }
  333. return STATUS_SUCCESS;
  334. }