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.

761 lines
19 KiB

  1. /*++
  2. Copyright (c) 2000 Microsoft Corporation
  3. Module Name:
  4. vfirplog.c
  5. Abstract:
  6. This module manages IRP logs for the verifier.
  7. Author:
  8. Adrian J. Oney (adriao) 09-May-1998
  9. Environment:
  10. Kernel mode
  11. Revision History:
  12. --*/
  13. //
  14. // Disable W4 level warnings generated by public headers.
  15. //
  16. #include "vfpragma.h"
  17. #include "..\io\iop.h" // Includes vfdef.h
  18. #include "viirplog.h"
  19. #ifdef ALLOC_PRAGMA
  20. #pragma alloc_text(PAGEVRFY, VfIrpLogInit)
  21. #pragma alloc_text(PAGEVRFY, ViIrpLogDatabaseFindPointer)
  22. #pragma alloc_text(PAGEVRFY, ViIrpLogExposeWmiCallback)
  23. #pragma alloc_text(PAGEVRFY, VfIrpLogRecordEvent)
  24. #pragma alloc_text(PAGEVRFY, VfIrpLogGetIrpDatabaseSiloCount)
  25. #pragma alloc_text(PAGEVRFY, VfIrpLogLockDatabase)
  26. #pragma alloc_text(PAGEVRFY, VfIrpLogRetrieveWmiData)
  27. #pragma alloc_text(PAGEVRFY, VfIrpLogUnlockDatabase)
  28. #pragma alloc_text(PAGEVRFY, VfIrpLogDeleteDeviceLogs)
  29. #endif
  30. PIRPLOG_HEAD ViIrpLogDatabase;
  31. KSPIN_LOCK ViIrpLogDatabaseLock;
  32. LONG ViIrpLogDdiLock = DDILOCK_UNREGISTERED;
  33. #define POOLTAG_IRPLOG_DATABASE 'dIfV'
  34. #define POOLTAG_IRPLOG_DATA 'eIfV'
  35. #define POOLTAG_IRPLOG_TEMP 'tIfV'
  36. #define POOLTAG_IRPLOG_WORKITEM 'wIfV'
  37. #define INSTANCE_NAME_PROLOG L"VERIFIER"
  38. VOID
  39. VfIrpLogInit(
  40. VOID
  41. )
  42. /*++
  43. Description:
  44. This routine initializes all the important structures we use to log IRPs.
  45. Arguments:
  46. None
  47. Return Value:
  48. None
  49. --*/
  50. {
  51. ULONG i;
  52. PAGED_CODE();
  53. KeInitializeSpinLock(&ViIrpLogDatabaseLock);
  54. //
  55. // As this is system startup code, it is one of the very few places
  56. // where it's ok to use MustSucceed.
  57. //
  58. ViIrpLogDatabase = (PIRPLOG_HEAD) ExAllocatePoolWithTag(
  59. NonPagedPoolMustSucceed,
  60. VI_IRPLOG_DATABASE_HASH_SIZE * sizeof(IRPLOG_HEAD),
  61. POOLTAG_IRPLOG_DATABASE
  62. );
  63. for(i=0; i < VI_IRPLOG_DATABASE_HASH_SIZE; i++) {
  64. ViIrpLogDatabase[i].Locked = FALSE;
  65. InitializeListHead(&ViIrpLogDatabase[i].ListHead);
  66. }
  67. }
  68. PIRPLOG_DATA
  69. FASTCALL
  70. ViIrpLogDatabaseFindPointer(
  71. IN PDEVICE_OBJECT DeviceObject,
  72. OUT PIRPLOG_HEAD *HashHead
  73. )
  74. /*++
  75. Description:
  76. This routine returns a pointer to a pointer to the per-device object.
  77. data. This function is meant to be called by other routines in this file.
  78. N.B. The verifier devobj database lock is assumed to be held by the caller.
  79. Arguments:
  80. DeviceObject - Device object to locate in the tracking table.
  81. HashHead - If return is non-null, points to the hash head that should
  82. be used to insert the tracking data.
  83. Return Value:
  84. IrpLogData iff found, NULL otherwise.
  85. --*/
  86. {
  87. PIRPLOG_DATA irpLogData;
  88. PLIST_ENTRY listEntry, listHead;
  89. UINT_PTR hashIndex;
  90. hashIndex = VI_IRPLOG_CALCULATE_DATABASE_HASH(DeviceObject);
  91. ASSERT_SPINLOCK_HELD(&ViIrpLogDatabaseLock);
  92. *HashHead = &ViIrpLogDatabase[hashIndex];
  93. listHead = &ViIrpLogDatabase[hashIndex].ListHead;
  94. for(listEntry = listHead;
  95. listEntry->Flink != listHead;
  96. listEntry = listEntry->Flink) {
  97. irpLogData = CONTAINING_RECORD(listEntry->Flink, IRPLOG_DATA, HashLink);
  98. if (irpLogData->DeviceObject == DeviceObject) {
  99. return irpLogData;
  100. }
  101. }
  102. return NULL;
  103. }
  104. VOID
  105. ViIrpLogExposeWmiCallback(
  106. IN PVOID Context
  107. )
  108. {
  109. PWORK_QUEUE_ITEM workQueueItem;
  110. PAGED_CODE();
  111. workQueueItem = (PWORK_QUEUE_ITEM) Context;
  112. VfDdiExposeWmiObjects();
  113. ViIrpLogDdiLock = DDILOCK_REGISTERED;
  114. ExFreePool(workQueueItem);
  115. }
  116. VOID
  117. VfIrpLogRecordEvent(
  118. IN PVERIFIER_SETTINGS_SNAPSHOT VerifierSettingsSnapshot,
  119. IN PDEVICE_OBJECT DeviceObject,
  120. IN PIRP Irp
  121. )
  122. {
  123. PIRPLOG_HEAD hashHead;
  124. PIRPLOG_DATA irpLogData;
  125. IRPLOG_SNAPSHOT irpLogSnapshot;
  126. PWORK_QUEUE_ITEM workQueueItem;
  127. KIRQL oldIrql;
  128. LONG oldVal;
  129. ULONG maxElementCount;
  130. ULONG elementCount;
  131. LOGICAL logEntry;
  132. if (!VfSettingsIsOptionEnabled(VerifierSettingsSnapshot,
  133. VERIFIER_OPTION_EXPOSE_IRP_HISTORY)) {
  134. return;
  135. }
  136. if (ViIrpLogDdiLock != DDILOCK_REGISTERED) {
  137. oldVal = InterlockedCompareExchange( &ViIrpLogDdiLock,
  138. DDILOCK_REGISTERING,
  139. DDILOCK_UNREGISTERED );
  140. if (oldVal == DDILOCK_UNREGISTERED) {
  141. workQueueItem = (PWORK_QUEUE_ITEM) ExAllocatePoolWithTag(
  142. NonPagedPool,
  143. sizeof(WORK_QUEUE_ITEM),
  144. POOLTAG_IRPLOG_WORKITEM
  145. );
  146. if (workQueueItem) {
  147. ExInitializeWorkItem(
  148. workQueueItem,
  149. ViIrpLogExposeWmiCallback,
  150. workQueueItem
  151. );
  152. ExQueueWorkItem(
  153. workQueueItem,
  154. DelayedWorkQueue
  155. );
  156. } else {
  157. ViIrpLogDdiLock = DDILOCK_UNREGISTERED;
  158. }
  159. }
  160. }
  161. ExAcquireSpinLock(&ViIrpLogDatabaseLock, &oldIrql);
  162. irpLogData = ViIrpLogDatabaseFindPointer(DeviceObject, &hashHead);
  163. if (hashHead->Locked) {
  164. //
  165. // The current logs are being drained. Since logs are lossy anyway,
  166. // dump this one on the floor.
  167. //
  168. ExReleaseSpinLock(&ViIrpLogDatabaseLock, oldIrql);
  169. return;
  170. }
  171. if (irpLogData == NULL) {
  172. VfSettingsGetValue(
  173. VerifierSettingsSnapshot,
  174. VERIFIER_VALUE_IRPLOG_COUNT,
  175. &maxElementCount
  176. );
  177. irpLogData = ExAllocatePoolWithTag(
  178. NonPagedPool,
  179. sizeof(IRPLOG_DATA)+(maxElementCount-1)*sizeof(IRPLOG_SNAPSHOT),
  180. POOLTAG_IRPLOG_DATA
  181. );
  182. if (irpLogData != NULL) {
  183. ObReferenceObject(DeviceObject);
  184. irpLogData->DeviceObject = DeviceObject;
  185. irpLogData->Flags = 0;
  186. irpLogData->DeviceType = DeviceObject->DeviceType;
  187. irpLogData->Head = 0;
  188. irpLogData->MaximumElementCount = maxElementCount;
  189. InsertHeadList(&hashHead->ListHead, &irpLogData->HashLink);
  190. }
  191. }
  192. if (irpLogData != NULL) {
  193. if (!(irpLogData->Flags & (IRPLOG_FLAG_DELETED | IRPLOG_FLAG_NAMELESS))) {
  194. if (irpLogData->Flags == IRPLOG_FLAG_FULL) {
  195. elementCount = irpLogData->MaximumElementCount;
  196. } else {
  197. elementCount = irpLogData->Head;
  198. }
  199. logEntry = VfMajorBuildIrpLogEntry(
  200. Irp,
  201. elementCount,
  202. &irpLogData->SnapshotArray[irpLogData->Head],
  203. &irpLogSnapshot
  204. );
  205. if (logEntry) {
  206. irpLogData->SnapshotArray[irpLogData->Head] = irpLogSnapshot;
  207. irpLogData->Head++;
  208. if (irpLogData->Head == irpLogData->MaximumElementCount) {
  209. irpLogData->Flags |= IRPLOG_FLAG_FULL;
  210. irpLogData->Head = 0;
  211. }
  212. }
  213. }
  214. }
  215. ExReleaseSpinLock(&ViIrpLogDatabaseLock, oldIrql);
  216. }
  217. ULONG
  218. VfIrpLogGetIrpDatabaseSiloCount(
  219. VOID
  220. )
  221. {
  222. return VI_IRPLOG_DATABASE_HASH_SIZE;
  223. }
  224. NTSTATUS
  225. VfIrpLogLockDatabase(
  226. IN ULONG SiloNumber
  227. )
  228. {
  229. NTSTATUS status;
  230. KIRQL oldIrql;
  231. ASSERT(SiloNumber < VI_IRPLOG_DATABASE_HASH_SIZE);
  232. //
  233. // Take the database offline. From this point on, changes can still be made
  234. // but new entries cannot be added, nor can old ones be removed from the
  235. // tree. We do this under a lock to ensure all current inserts/removals
  236. // have drained with respect to the state change.
  237. //
  238. ExAcquireSpinLock(&ViIrpLogDatabaseLock, &oldIrql);
  239. if (ViIrpLogDatabase[SiloNumber].Locked) {
  240. //
  241. // Reentrancy attempt - we don't try to do anything clever.
  242. //
  243. status = STATUS_RETRY;
  244. } else {
  245. ViIrpLogDatabase[SiloNumber].Locked = TRUE;
  246. status = STATUS_SUCCESS;
  247. }
  248. ExReleaseSpinLock(&ViIrpLogDatabaseLock, oldIrql);
  249. return status;
  250. }
  251. NTSTATUS
  252. VfIrpLogRetrieveWmiData(
  253. IN ULONG SiloNumber,
  254. OUT PUCHAR OutputBuffer OPTIONAL,
  255. OUT ULONG *OffsetInstanceNameOffsets,
  256. OUT ULONG *InstanceCount,
  257. OUT ULONG *DataBlockOffset,
  258. OUT ULONG *TotalRequiredSize
  259. )
  260. {
  261. PIRPLOG_DATA irpLogData;
  262. PLIST_ENTRY listEntry, listHead;
  263. ULONG instances;
  264. POBJECT_NAME_INFORMATION objectName;
  265. ULONG currentNameSize, neededNameSize;
  266. ULONG totalDataSize;
  267. ULONG nameOffsetArrayOffset;
  268. ULONG instanceLengthArrayOffset;
  269. ULONG nameStringBufferOffset;
  270. ULONG instanceDataOffset;
  271. ULONG individualStringLengthInBytes;
  272. ULONG individualStringLengthInChars;
  273. ULONG elementCount;
  274. PULONG nameOffsetBuffer;
  275. POFFSETINSTANCEDATAANDLENGTH instanceLengthBuffer;
  276. PUSHORT nameStringBuffer;
  277. PUCHAR instanceDataBuffer;
  278. NTSTATUS status;
  279. //
  280. // The irp log database must be locked for this query.
  281. //
  282. ASSERT(SiloNumber < VI_IRPLOG_DATABASE_HASH_SIZE);
  283. ASSERT(ViIrpLogDatabase[SiloNumber].Locked);
  284. //
  285. // Preinit for error.
  286. //
  287. *OffsetInstanceNameOffsets = 0;
  288. *InstanceCount = 0;
  289. *DataBlockOffset = 0;
  290. *TotalRequiredSize = 0;
  291. //
  292. // Allocate an initial buffer.
  293. //
  294. currentNameSize = sizeof(OBJECT_NAME_INFORMATION);
  295. objectName = ExAllocatePoolWithTag(
  296. PagedPool,
  297. currentNameSize,
  298. POOLTAG_IRPLOG_TEMP
  299. );
  300. if (objectName == NULL) {
  301. return STATUS_INSUFFICIENT_RESOURCES;
  302. }
  303. //
  304. // Walk through the database and start retrieving information. First count
  305. // the instances.
  306. //
  307. instances = 0;
  308. listHead = &ViIrpLogDatabase[SiloNumber].ListHead;
  309. for(listEntry = listHead;
  310. listEntry->Flink != listHead;
  311. listEntry = listEntry->Flink) {
  312. irpLogData = CONTAINING_RECORD(listEntry->Flink, IRPLOG_DATA, HashLink);
  313. #ifdef MAX_INSTANCE_COUNT
  314. if (instances == MAX_INSTANCE_COUNT) {
  315. break;
  316. }
  317. #endif
  318. instances++;
  319. }
  320. //
  321. // First, since we have dynamic named, we need to allocate space for the
  322. // ULONG-sized array of offset pointers to each string.
  323. //
  324. //
  325. // The buffer will look like this:
  326. //
  327. // [WNODE_ALL_DATA]
  328. // [Array of per-instance DataOffset+DataLength entries)
  329. // [Array of per-instance NameOffset entries]
  330. // [names]
  331. // [data]
  332. //
  333. instanceLengthArrayOffset = FIELD_OFFSET(WNODE_ALL_DATA, OffsetInstanceDataAndLength);
  334. nameOffsetArrayOffset = instanceLengthArrayOffset + instances*sizeof(OFFSETINSTANCEDATAANDLENGTH);
  335. nameStringBufferOffset = nameOffsetArrayOffset + instances*sizeof(ULONG);
  336. nameOffsetBuffer = (PULONG) (OutputBuffer + nameOffsetArrayOffset);
  337. instanceLengthBuffer = (POFFSETINSTANCEDATAANDLENGTH) (OutputBuffer + instanceLengthArrayOffset);
  338. nameStringBuffer = (PUSHORT) (OutputBuffer + nameStringBufferOffset);
  339. //
  340. // So far the required size only accounts for the array of offsets to names
  341. //
  342. totalDataSize = nameStringBufferOffset;
  343. //
  344. // Now start collecting the names.
  345. //
  346. status = STATUS_SUCCESS;
  347. instances = 0;
  348. listHead = &ViIrpLogDatabase[SiloNumber].ListHead;
  349. for(listEntry = listHead;
  350. listEntry->Flink != listHead;
  351. listEntry = listEntry->Flink) {
  352. irpLogData = CONTAINING_RECORD(listEntry->Flink, IRPLOG_DATA, HashLink);
  353. //
  354. // Retrieve the name
  355. //
  356. status = ObQueryNameString(
  357. irpLogData->DeviceObject,
  358. objectName,
  359. currentNameSize,
  360. &neededNameSize
  361. );
  362. if (status == STATUS_INFO_LENGTH_MISMATCH) {
  363. ExFreePool(objectName);
  364. objectName = ExAllocatePoolWithTag(
  365. PagedPool,
  366. neededNameSize,
  367. POOLTAG_IRPLOG_TEMP
  368. );
  369. if (objectName == NULL) {
  370. status = STATUS_INSUFFICIENT_RESOURCES;
  371. } else {
  372. currentNameSize = neededNameSize;
  373. status = ObQueryNameString(
  374. irpLogData->DeviceObject,
  375. objectName,
  376. currentNameSize,
  377. &neededNameSize
  378. );
  379. }
  380. }
  381. if (!NT_SUCCESS(status)) {
  382. break;
  383. }
  384. if (objectName->Name.Length == 0) {
  385. irpLogData->Flags |= IRPLOG_FLAG_NAMELESS;
  386. continue;
  387. }
  388. #ifdef MAX_INSTANCE_COUNT
  389. if (instances == MAX_INSTANCE_COUNT) {
  390. break;
  391. }
  392. #endif
  393. instances++;
  394. //
  395. // Write the appropriate offset into the name offset array
  396. //
  397. if (ARGUMENT_PRESENT(OutputBuffer)) {
  398. *nameOffsetBuffer = totalDataSize;
  399. }
  400. nameOffsetBuffer++;
  401. //
  402. // Add in memory for each "counted" string. WMI counted strings are
  403. // of the form [USHORT LenInBytesIncludingTerminator]
  404. // [WCHAR Array w/NULL terminator]
  405. //
  406. // The string is of the form VERIFIER\Device\Foo (The terminating NULL
  407. // is accounted for by sizeof().
  408. //
  409. individualStringLengthInBytes = objectName->Name.Length + sizeof(INSTANCE_NAME_PROLOG);
  410. individualStringLengthInChars = individualStringLengthInBytes/sizeof(WCHAR);
  411. //
  412. // Write out the counted string, starting with the length
  413. //
  414. ASSERT(OutputBuffer + totalDataSize == (PUCHAR) nameStringBuffer);
  415. if (ARGUMENT_PRESENT(OutputBuffer)) {
  416. *nameStringBuffer = (USHORT) individualStringLengthInBytes;
  417. }
  418. nameStringBuffer++;
  419. totalDataSize += sizeof(USHORT);
  420. if (ARGUMENT_PRESENT(OutputBuffer)) {
  421. RtlCopyMemory(
  422. nameStringBuffer,
  423. INSTANCE_NAME_PROLOG,
  424. sizeof(INSTANCE_NAME_PROLOG)-sizeof(UNICODE_NULL)
  425. );
  426. RtlCopyMemory(
  427. nameStringBuffer + ((sizeof(INSTANCE_NAME_PROLOG) - sizeof(UNICODE_NULL))/sizeof(WCHAR)),
  428. objectName->Name.Buffer,
  429. objectName->Name.Length
  430. );
  431. nameStringBuffer[individualStringLengthInChars-1] = UNICODE_NULL;
  432. }
  433. nameStringBuffer += individualStringLengthInChars;
  434. totalDataSize += individualStringLengthInBytes;
  435. }
  436. if (objectName) {
  437. ExFreePool(objectName);
  438. }
  439. if (!NT_SUCCESS(status)) {
  440. return status;
  441. }
  442. //
  443. // Now collect the instance data
  444. //
  445. totalDataSize = ALIGN_UP_ULONG(totalDataSize, 8);
  446. instanceDataOffset = totalDataSize;
  447. instanceDataBuffer = (OutputBuffer + instanceDataOffset);
  448. instances = 0;
  449. listHead = &ViIrpLogDatabase[SiloNumber].ListHead;
  450. for(listEntry = listHead;
  451. listEntry->Flink != listHead;
  452. listEntry = listEntry->Flink) {
  453. irpLogData = CONTAINING_RECORD(listEntry->Flink, IRPLOG_DATA, HashLink);
  454. if (irpLogData->Flags & IRPLOG_FLAG_NAMELESS) {
  455. continue;
  456. }
  457. #ifdef MAX_INSTANCE_COUNT
  458. if (instances == MAX_INSTANCE_COUNT) {
  459. break;
  460. }
  461. #endif
  462. instances++;
  463. if (irpLogData->Flags & IRPLOG_FLAG_FULL) {
  464. elementCount = irpLogData->MaximumElementCount;
  465. } else {
  466. elementCount = irpLogData->Head;
  467. }
  468. if (ARGUMENT_PRESENT(OutputBuffer)) {
  469. //
  470. // Update the array of per-instance Offset/Length information
  471. //
  472. instanceLengthBuffer->OffsetInstanceData = totalDataSize;
  473. instanceLengthBuffer->LengthInstanceData =
  474. sizeof(ULONG)*2 + (elementCount * sizeof(IRPLOG_SNAPSHOT));
  475. instanceLengthBuffer++;
  476. //
  477. // Write out the device type.
  478. //
  479. *((PULONG) instanceDataBuffer) = irpLogData->DeviceType;
  480. instanceDataBuffer += sizeof(ULONG);
  481. //
  482. // Write out the instance data count
  483. //
  484. *((PULONG) instanceDataBuffer) = elementCount;
  485. instanceDataBuffer += sizeof(ULONG);
  486. //
  487. // Don't bother with reordering the data appropriately. Also note
  488. // that we have 8 byte alignment here - very important!!!
  489. //
  490. RtlCopyMemory(
  491. instanceDataBuffer,
  492. irpLogData->SnapshotArray,
  493. elementCount * sizeof(IRPLOG_SNAPSHOT)
  494. );
  495. instanceDataBuffer += elementCount * sizeof(IRPLOG_SNAPSHOT);
  496. }
  497. totalDataSize += sizeof(ULONG)*2;
  498. totalDataSize += elementCount * sizeof(IRPLOG_SNAPSHOT);
  499. }
  500. *OffsetInstanceNameOffsets = nameOffsetArrayOffset;
  501. *InstanceCount = instances;
  502. *DataBlockOffset = instanceDataOffset;
  503. *TotalRequiredSize = totalDataSize;
  504. return STATUS_SUCCESS;
  505. }
  506. VOID
  507. VfIrpLogUnlockDatabase(
  508. IN ULONG SiloNumber
  509. )
  510. {
  511. KIRQL oldIrql;
  512. PIRPLOG_DATA irpLogData;
  513. PLIST_ENTRY listEntry, listHead;
  514. ASSERT(SiloNumber < VI_IRPLOG_DATABASE_HASH_SIZE);
  515. //
  516. // Reenable logging to present devices
  517. //
  518. ViIrpLogDatabase[SiloNumber].Locked = FALSE;
  519. //
  520. // Clean up any lingering deleted device data
  521. //
  522. ExAcquireSpinLock(&ViIrpLogDatabaseLock, &oldIrql);
  523. listHead = &ViIrpLogDatabase[SiloNumber].ListHead;
  524. for(listEntry = listHead;
  525. listEntry->Flink != listHead;
  526. listEntry = listEntry->Flink) {
  527. irpLogData = CONTAINING_RECORD(listEntry->Flink, IRPLOG_DATA, HashLink);
  528. if (irpLogData->Flags & IRPLOG_FLAG_DELETED) {
  529. ObDereferenceObject(irpLogData->DeviceObject);
  530. RemoveEntryList(&irpLogData->HashLink);
  531. ExFreePool(irpLogData);
  532. }
  533. }
  534. ExReleaseSpinLock(&ViIrpLogDatabaseLock, oldIrql);
  535. }
  536. VOID
  537. VfIrpLogDeleteDeviceLogs(
  538. IN PDEVICE_OBJECT DeviceObject
  539. )
  540. {
  541. PIRPLOG_DATA irpLogData;
  542. PIRPLOG_HEAD hashHead;
  543. KIRQL oldIrql;
  544. ExAcquireSpinLock(&ViIrpLogDatabaseLock, &oldIrql);
  545. irpLogData = ViIrpLogDatabaseFindPointer(DeviceObject, &hashHead);
  546. if (irpLogData != NULL) {
  547. if (!hashHead->Locked) {
  548. ObDereferenceObject(irpLogData->DeviceObject);
  549. RemoveEntryList(&irpLogData->HashLink);
  550. ExFreePool(irpLogData);
  551. } else {
  552. irpLogData->Flags |= IRPLOG_FLAG_DELETED;
  553. }
  554. }
  555. ExReleaseSpinLock(&ViIrpLogDatabaseLock, oldIrql);
  556. }