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.

3507 lines
96 KiB

  1. /*++
  2. Copyright (c) 2000-2002 Microsoft Corporation
  3. Module Name:
  4. rawlog.c (HTTP.SYS Binary Logging)
  5. Abstract:
  6. This module implements the centralized raw logging
  7. format. Internet Binary Logs (file format).
  8. Author:
  9. Ali E. Turkoglu (aliTu) 04-Oct-2001
  10. Revision History:
  11. ---
  12. --*/
  13. #include "precomp.h"
  14. #include "rawlogp.h"
  15. //
  16. // Generic Private globals.
  17. //
  18. UL_BINARY_LOG_FILE_ENTRY g_BinaryLogEntry;
  19. ULONG g_BinaryLogEntryCount = 0;
  20. BOOLEAN g_InitBinaryLogCalled = FALSE;
  21. #ifdef ALLOC_PRAGMA
  22. #pragma alloc_text( INIT, UlInitializeBinaryLog )
  23. #pragma alloc_text( PAGE, UlpCreateBinaryLogFile )
  24. #pragma alloc_text( PAGE, UlpDisableBinaryEntry )
  25. #pragma alloc_text( PAGE, UlpRecycleBinaryLogFile )
  26. #pragma alloc_text( PAGE, UlCaptureRawLogData )
  27. #pragma alloc_text( PAGE, UlpRawCopyLogHeader )
  28. #pragma alloc_text( PAGE, UlpRawCopyLogFooter )
  29. #pragma alloc_text( PAGE, UlpRawCopyForLogCacheMiss )
  30. #pragma alloc_text( PAGE, UlRawLogHttpHit )
  31. #pragma alloc_text( PAGE, UlpRawCopyForLogCacheHit )
  32. #pragma alloc_text( PAGE, UlRawLogHttpCacheHit )
  33. #pragma alloc_text( PAGE, UlpWriteToRawLogFileShared )
  34. #pragma alloc_text( PAGE, UlpWriteToRawLogFileExclusive )
  35. #pragma alloc_text( PAGE, UlpWriteToRawLogFile )
  36. #pragma alloc_text( PAGE, UlpBinaryBufferTimerHandler )
  37. #pragma alloc_text( PAGE, UlHandleCacheFlushedNotification )
  38. #pragma alloc_text( PAGE, UlpEventLogRawWriteFailure )
  39. #pragma alloc_text( PAGE, UlpWriteToRawLogFileDebug )
  40. #endif // ALLOC_PRAGMA
  41. #if 0
  42. NOT PAGEABLE -- UlpBinaryLogTimerDpcRoutine
  43. NOT PAGEABLE -- UlpBinaryLogBufferTimerDpcRoutine
  44. NOT PAGEABLE -- UlpBinaryLogTimerHandler
  45. NOT PAGEABLE -- UlCreateBinaryLogEntry
  46. NOT PAGEABLE -- UlTerminateBinaryLog
  47. #endif
  48. //
  49. // Public functions.
  50. //
  51. /***************************************************************************++
  52. Routine Description:
  53. Init the global binary logging entry and its fields.
  54. --***************************************************************************/
  55. NTSTATUS
  56. UlInitializeBinaryLog (
  57. VOID
  58. )
  59. {
  60. NTSTATUS Status = STATUS_SUCCESS;
  61. PAGED_CODE();
  62. ASSERT(!g_InitBinaryLogCalled);
  63. if (!g_InitBinaryLogCalled)
  64. {
  65. //
  66. // Init the global binary logging entry.
  67. //
  68. RtlZeroMemory(
  69. (PCHAR)&g_BinaryLogEntry, sizeof(UL_BINARY_LOG_FILE_ENTRY));
  70. g_BinaryLogEntry.Signature = UL_BINARY_LOG_FILE_ENTRY_POOL_TAG;
  71. UlInitializePushLock(
  72. &g_BinaryLogEntry.PushLock,
  73. "BinaryLogEntryPushLock",
  74. 0,
  75. UL_BINARY_LOG_FILE_ENTRY_POOL_TAG
  76. );
  77. // Initialize the Recycle timer
  78. g_BinaryLogEntry.Timer.Initialized = TRUE;
  79. g_BinaryLogEntry.Timer.Started = FALSE;
  80. g_BinaryLogEntry.Timer.Period = -1;
  81. g_BinaryLogEntry.Timer.PeriodType = UlLogTimerPeriodNone;
  82. UlInitializeSpinLock(
  83. &g_BinaryLogEntry.Timer.SpinLock, "BinaryLogEntryTimerSpinlock");
  84. KeInitializeDpc(
  85. &g_BinaryLogEntry.Timer.DpcObject,
  86. &UlpBinaryLogTimerDpcRoutine,
  87. NULL
  88. );
  89. KeInitializeTimer(&g_BinaryLogEntry.Timer.Timer);
  90. // Initialize the buffer flush timer
  91. g_BinaryLogEntry.BufferTimer.Initialized = TRUE;
  92. g_BinaryLogEntry.BufferTimer.Started = FALSE;
  93. g_BinaryLogEntry.BufferTimer.Period = -1;
  94. g_BinaryLogEntry.BufferTimer.PeriodType = UlLogTimerPeriodNone;
  95. UlInitializeSpinLock(
  96. &g_BinaryLogEntry.BufferTimer.SpinLock,
  97. "BinaryLogEntryBufferTimerSpinLock" );
  98. KeInitializeDpc(
  99. &g_BinaryLogEntry.BufferTimer.DpcObject, // DPC object
  100. &UlpBinaryBufferTimerDpcRoutine, // DPC routine
  101. NULL // context
  102. );
  103. KeInitializeTimer(&g_BinaryLogEntry.BufferTimer.Timer);
  104. UlInitializeWorkItem(&g_BinaryLogEntry.WorkItem);
  105. g_InitBinaryLogCalled = TRUE;
  106. UlTrace(BINARY_LOGGING,
  107. ("Http!UlInitializeBinaryLog: g_BinaryLogEntry @ (%p) .\n",
  108. &g_BinaryLogEntry
  109. ));
  110. }
  111. return Status;
  112. }
  113. /***************************************************************************++
  114. Routine Description:
  115. Terminates the binary logging entry and its fields.
  116. --***************************************************************************/
  117. VOID
  118. UlTerminateBinaryLog(
  119. VOID
  120. )
  121. {
  122. KIRQL OldIrql;
  123. if (g_InitBinaryLogCalled)
  124. {
  125. PUL_LOG_TIMER pTimer = &g_BinaryLogEntry.Timer;
  126. PUL_LOG_TIMER pBufferTimer = &g_BinaryLogEntry.BufferTimer;
  127. //
  128. // Terminate the recycle timer
  129. //
  130. UlAcquireSpinLock(&pTimer->SpinLock, &OldIrql);
  131. pTimer->Initialized = FALSE;
  132. KeCancelTimer(&pTimer->Timer);
  133. UlReleaseSpinLock(&pTimer->SpinLock, OldIrql);
  134. UlAcquireSpinLock(&pBufferTimer->SpinLock, &OldIrql);
  135. pBufferTimer->Initialized = FALSE;
  136. KeCancelTimer(&pBufferTimer->Timer);
  137. UlReleaseSpinLock(&pBufferTimer->SpinLock, OldIrql);
  138. //
  139. // Delete the push lock
  140. //
  141. UlDeletePushLock(&g_BinaryLogEntry.PushLock);
  142. g_InitBinaryLogCalled = FALSE;
  143. }
  144. }
  145. /***************************************************************************++
  146. Routine Description:
  147. Queues a passive worker for the lowered irql.
  148. Arguments:
  149. Ignored
  150. --***************************************************************************/
  151. VOID
  152. UlpBinaryBufferTimerDpcRoutine(
  153. PKDPC Dpc,
  154. PVOID DeferredContext,
  155. PVOID SystemArgument1,
  156. PVOID SystemArgument2
  157. )
  158. {
  159. PUL_LOG_TIMER pTimer = &g_BinaryLogEntry.BufferTimer;
  160. PUL_WORK_ITEM pWorkItem = NULL;
  161. //
  162. // Parameters are ignored.
  163. //
  164. UNREFERENCED_PARAMETER(Dpc);
  165. UNREFERENCED_PARAMETER(DeferredContext);
  166. UNREFERENCED_PARAMETER(SystemArgument1);
  167. UNREFERENCED_PARAMETER(SystemArgument2);
  168. UlAcquireSpinLockAtDpcLevel(&pTimer->SpinLock);
  169. if (pTimer->Initialized == TRUE)
  170. {
  171. //
  172. // It's not possible to acquire the resource which protects
  173. // the binary entry at DISPATCH_LEVEL therefore we will queue
  174. // a work item for this.
  175. //
  176. pWorkItem = (PUL_WORK_ITEM) UL_ALLOCATE_POOL(
  177. NonPagedPool,
  178. sizeof(*pWorkItem),
  179. UL_WORK_ITEM_POOL_TAG
  180. );
  181. if (pWorkItem)
  182. {
  183. UlInitializeWorkItem(pWorkItem);
  184. UL_QUEUE_WORK_ITEM(pWorkItem, &UlpBinaryBufferTimerHandler);
  185. }
  186. else
  187. {
  188. UlTrace(BINARY_LOGGING,
  189. ("Http!UlBinaryLogBufferTimerDpcRoutine: Not enough memory.\n"));
  190. }
  191. }
  192. UlReleaseSpinLockFromDpcLevel(&pTimer->SpinLock);
  193. }
  194. /***************************************************************************++
  195. Routine Description:
  196. Flushes or recycles the binary log file.
  197. Arguments:
  198. PUL_WORK_ITEM - Ignored but cleaned up at the end
  199. --***************************************************************************/
  200. VOID
  201. UlpBinaryBufferTimerHandler(
  202. IN PUL_WORK_ITEM pWorkItem
  203. )
  204. {
  205. NTSTATUS Status = STATUS_SUCCESS;
  206. PUL_BINARY_LOG_FILE_ENTRY pEntry = &g_BinaryLogEntry;
  207. PAGED_CODE();
  208. UlTrace(BINARY_LOGGING,
  209. ("Http!UlpBinaryBufferTimerHandler: Checking the BinaryLogEntry. \n"));
  210. UlAcquirePushLockExclusive(&pEntry->PushLock);
  211. if (pEntry->Flags.Active)
  212. {
  213. if (pEntry->Flags.RecyclePending)
  214. {
  215. //
  216. // Try to resurrect it back.
  217. //
  218. Status = UlpRecycleBinaryLogFile(pEntry);
  219. }
  220. else
  221. {
  222. //
  223. // Everything is fine simply flush.
  224. //
  225. if (NULL != pEntry->LogBuffer && 0 != pEntry->LogBuffer->BufferUsed)
  226. {
  227. Status = UlpFlushRawLogFile(pEntry);
  228. }
  229. else
  230. {
  231. //
  232. // Inactivity management. Update the counter.
  233. // If entry was inactive over 15 minutes, close it.
  234. //
  235. ASSERT( pEntry->TimeToClose > 0 );
  236. if (pEntry->TimeToClose == 1)
  237. {
  238. if (pEntry->Period == HttpLoggingPeriodMaxSize)
  239. {
  240. pEntry->Flags.StaleSequenceNumber = 1;
  241. }
  242. else
  243. {
  244. pEntry->Flags.StaleTimeToExpire = 1;
  245. }
  246. Status = UlpDisableBinaryEntry(pEntry);
  247. }
  248. else
  249. {
  250. pEntry->TimeToClose -= 1;
  251. }
  252. }
  253. }
  254. }
  255. UlReleasePushLockExclusive(&pEntry->PushLock);
  256. //
  257. // Free the memory allocated (ByDpcRoutine above) for
  258. // this work item.
  259. //
  260. UL_FREE_POOL(pWorkItem, UL_WORK_ITEM_POOL_TAG);
  261. }
  262. /***************************************************************************++
  263. Routine Description:
  264. Allocates and queues a work item to do the the actual work at lowered irql
  265. Arguments:
  266. Ignored
  267. --***************************************************************************/
  268. VOID
  269. UlpBinaryLogTimerDpcRoutine(
  270. PKDPC Dpc,
  271. PVOID DeferredContext,
  272. PVOID SystemArgument1,
  273. PVOID SystemArgument2
  274. )
  275. {
  276. PUL_LOG_TIMER pTimer = &g_BinaryLogEntry.Timer;
  277. PUL_WORK_ITEM pWorkItem = &g_BinaryLogEntry.WorkItem;
  278. //
  279. // Parameters are ignored.
  280. //
  281. UNREFERENCED_PARAMETER(Dpc);
  282. UNREFERENCED_PARAMETER(DeferredContext);
  283. UNREFERENCED_PARAMETER(SystemArgument1);
  284. UNREFERENCED_PARAMETER(SystemArgument2);
  285. UlAcquireSpinLockAtDpcLevel(&pTimer->SpinLock);
  286. if (pTimer->Initialized == TRUE)
  287. {
  288. UL_QUEUE_WORK_ITEM(pWorkItem, &UlpBinaryLogTimerHandler);
  289. }
  290. UlReleaseSpinLockFromDpcLevel(&pTimer->SpinLock);
  291. }
  292. /***************************************************************************++
  293. Routine Description:
  294. Passive worker for the BinaryLog recycling.
  295. Arguments:
  296. PUL_WORK_ITEM - Ignored.
  297. --***************************************************************************/
  298. VOID
  299. UlpBinaryLogTimerHandler(
  300. IN PUL_WORK_ITEM pWorkItem
  301. )
  302. {
  303. NTSTATUS Status;
  304. KIRQL OldIrql;
  305. BOOLEAN Picked = FALSE;
  306. PUL_BINARY_LOG_FILE_ENTRY pEntry = &g_BinaryLogEntry;
  307. UNREFERENCED_PARAMETER(pWorkItem);
  308. UlTrace(BINARY_LOGGING,
  309. ("\nHttp!UlpBinaryLogTimerHandler: Recycling ...\n\n"));
  310. UlAcquirePushLockExclusive(&pEntry->PushLock);
  311. switch(pEntry->Timer.PeriodType)
  312. {
  313. case UlLogTimerPeriodGMT:
  314. Picked = TRUE; // TODO: (pEntry->Flags.LocaltimeRollover == 0);
  315. break;
  316. case UlLogTimerPeriodLocal:
  317. Picked = FALSE;
  318. break;
  319. case UlLogTimerPeriodBoth:
  320. Picked = TRUE;
  321. break;
  322. default:
  323. ASSERT(!"Unexpected timer period type !\n");
  324. break;
  325. }
  326. if (Picked == TRUE &&
  327. pEntry->Flags.Active &&
  328. pEntry->Period != HttpLoggingPeriodMaxSize
  329. )
  330. {
  331. if (pEntry->TimeToExpire == 1)
  332. {
  333. //
  334. // Disable the entry and postpone the recycle to the next
  335. // incoming request. Lazy file creation.
  336. //
  337. pEntry->Flags.StaleTimeToExpire = 1;
  338. Status = UlpDisableBinaryEntry(pEntry);
  339. }
  340. else
  341. {
  342. //
  343. // Decrement the hourly counter.
  344. //
  345. pEntry->TimeToExpire -= 1;
  346. }
  347. }
  348. //
  349. // CODEWORK:
  350. // When we start handling multiple binary log file entries and the
  351. // pEntry is no longer pointing to a global static , following set
  352. // needs to be moved to the inside the lock.See ullog.c for similar
  353. // usage.
  354. //
  355. UlReleasePushLockExclusive(&pEntry->PushLock);
  356. //
  357. // Now reset the timer for the next hour.
  358. //
  359. UlAcquireSpinLock(&pEntry->Timer.SpinLock, &OldIrql);
  360. if (pEntry->Timer.Initialized == TRUE)
  361. {
  362. UlSetLogTimer(&pEntry->Timer);
  363. }
  364. UlReleaseSpinLock(&pEntry->Timer.SpinLock, OldIrql);
  365. }
  366. /***************************************************************************++
  367. Routine Description:
  368. When logging configuration happens we init the entry but do not create the
  369. binary log file itself yet. That will be created when the first request
  370. comes in. Please look at UlpCreateBinaryLogFile.
  371. Arguments:
  372. pControlChannel - Supplies the necessary information for constructing the
  373. log file entry.
  374. pUserConfig - Binary Logging config from the user.
  375. --***************************************************************************/
  376. NTSTATUS
  377. UlCreateBinaryLogEntry(
  378. IN OUT PUL_CONTROL_CHANNEL pControlChannel,
  379. IN PHTTP_CONTROL_CHANNEL_BINARY_LOGGING pUserConfig
  380. )
  381. {
  382. KIRQL OldIrql;
  383. NTSTATUS Status = STATUS_SUCCESS;
  384. PUL_BINARY_LOG_FILE_ENTRY pEntry = &g_BinaryLogEntry;
  385. PHTTP_CONTROL_CHANNEL_BINARY_LOGGING pConfig;
  386. //
  387. // Sanity check.
  388. //
  389. PAGED_CODE();
  390. ASSERT(IS_VALID_CONTROL_CHANNEL(pControlChannel));
  391. // TODO: We have to handle multiple binary log files.
  392. if (0 != InterlockedExchange((PLONG)&g_BinaryLogEntryCount, 1))
  393. {
  394. return STATUS_NOT_SUPPORTED;
  395. }
  396. UlAcquirePushLockExclusive(&pEntry->PushLock);
  397. ASSERT(pControlChannel->pBinaryLogEntry == NULL);
  398. //
  399. // Save the user logging info to the control channel.
  400. // Mark the state of the control channel binary logging.
  401. //
  402. pControlChannel->BinaryLoggingConfig = *pUserConfig;
  403. pConfig = &pControlChannel->BinaryLoggingConfig;
  404. pConfig->LogFileDir.Buffer =
  405. (PWSTR) UL_ALLOCATE_ARRAY(
  406. PagedPool,
  407. UCHAR,
  408. pConfig->LogFileDir.MaximumLength,
  409. UL_CG_LOGDIR_POOL_TAG
  410. );
  411. if (pConfig->LogFileDir.Buffer == NULL)
  412. {
  413. Status = STATUS_NO_MEMORY;
  414. goto end;
  415. }
  416. RtlCopyMemory(
  417. pConfig->LogFileDir.Buffer,
  418. pUserConfig->LogFileDir.Buffer,
  419. pUserConfig->LogFileDir.MaximumLength
  420. );
  421. pConfig->Flags.Present = 1;
  422. pConfig->LoggingEnabled = TRUE;
  423. //
  424. // Now set the fields on the binary log entry accordingly.
  425. //
  426. pEntry->Period = (HTTP_LOGGING_PERIOD) pConfig->LogPeriod;
  427. pEntry->TruncateSize = pConfig->LogFileTruncateSize;
  428. pEntry->TimeToExpire = 0;
  429. pEntry->TimeToClose = DEFAULT_MAX_FILE_IDLE_TIME;
  430. pEntry->SequenceNumber = 1;
  431. pEntry->TotalWritten.QuadPart = (ULONGLONG) 0;
  432. //
  433. // Start the recycle timer as soon as the binary logging
  434. // settings get configured. And buffer flush timer as well
  435. //
  436. UlAcquireSpinLock(&pEntry->Timer.SpinLock,&OldIrql);
  437. if (pEntry->Timer.Started == FALSE)
  438. {
  439. UlSetLogTimer(&pEntry->Timer);
  440. pEntry->Timer.Started = TRUE;
  441. }
  442. UlReleaseSpinLock(&pEntry->Timer.SpinLock,OldIrql);
  443. UlAcquireSpinLock(&pEntry->BufferTimer.SpinLock, &OldIrql);
  444. if (pEntry->BufferTimer.Started == FALSE)
  445. {
  446. UlSetBufferTimer(&pEntry->BufferTimer);
  447. pEntry->BufferTimer.Started = TRUE;
  448. }
  449. UlReleaseSpinLock(&pEntry->BufferTimer.SpinLock, OldIrql);
  450. //
  451. // Now remember the binary log entry in the control channel.
  452. //
  453. pControlChannel->pBinaryLogEntry = pEntry;
  454. UlTrace(BINARY_LOGGING,("Http!UlCreateBinaryLogEntry: pEntry %p for %S\n",
  455. pEntry,
  456. pConfig->LogFileDir.Buffer
  457. ));
  458. end:
  459. if (!NT_SUCCESS(Status))
  460. {
  461. UlTrace(BINARY_LOGGING,
  462. ("Http!UlCreateBinaryLogEntry: dir %S failure %08lx\n",
  463. pConfig->LogFileDir.Buffer,
  464. Status
  465. ));
  466. //
  467. // Restore the logging disabled state on the control channel,
  468. // free the memory which was allocated for the dir.
  469. //
  470. if (pConfig->LogFileDir.Buffer)
  471. {
  472. UL_FREE_POOL(pConfig->LogFileDir.Buffer,
  473. UL_CG_LOGDIR_POOL_TAG
  474. );
  475. }
  476. pConfig->LogFileDir.Buffer = NULL;
  477. ASSERT(pControlChannel->pBinaryLogEntry == NULL);
  478. pConfig->Flags.Present = 0;
  479. pConfig->LoggingEnabled = FALSE;
  480. }
  481. UlReleasePushLockExclusive(&pEntry->PushLock);
  482. return Status;
  483. }
  484. /***************************************************************************++
  485. Routine Description:
  486. Create a new binary log file or open an existing one. The fully qualified
  487. file name should be in the binary log entry.
  488. Arguments:
  489. pEntry : Corresponding entry that we are closing and opening
  490. the log files for.
  491. pDirectory : User passed directory which is stored in the control channel
  492. --***************************************************************************/
  493. NTSTATUS
  494. UlpCreateBinaryLogFile(
  495. IN OUT PUL_BINARY_LOG_FILE_ENTRY pEntry,
  496. IN PUNICODE_STRING pDirectory
  497. )
  498. {
  499. NTSTATUS Status = STATUS_SUCCESS;
  500. //
  501. // Sanity check.
  502. //
  503. PAGED_CODE();
  504. ASSERT(IS_VALID_BINARY_LOG_FILE_ENTRY(pEntry));
  505. ASSERT(pDirectory);
  506. UlTrace(BINARY_LOGGING,
  507. ("Http!UlpCreateBinaryLogFile: pEntry %p\n", pEntry));
  508. //
  509. // Build the fully qualified file name.
  510. //
  511. Status = UlRefreshFileName(pDirectory,
  512. &pEntry->FileName,
  513. &pEntry->pShortName
  514. );
  515. if (!NT_SUCCESS(Status))
  516. {
  517. return Status;
  518. }
  519. //
  520. // SequenceNumber is stale because we have to scan the existing directory
  521. // the first time we open a file. TimeToExpire is stale because we need to
  522. // calculate the it for the first time.
  523. //
  524. pEntry->Flags.StaleSequenceNumber = 1;
  525. pEntry->Flags.StaleTimeToExpire = 1;
  526. //
  527. // After that Recycle does the whole job for us.
  528. //
  529. Status = UlpRecycleBinaryLogFile(pEntry);
  530. if (!NT_SUCCESS(Status))
  531. {
  532. UlTrace(BINARY_LOGGING,
  533. ("Http!UlpCreateBinaryLogFile: Filename: %S Failure %08lx\n",
  534. pEntry->FileName.Buffer,
  535. Status
  536. ));
  537. }
  538. return Status;
  539. }
  540. /***************************************************************************++
  541. Routine Description:
  542. Writes an event log to system log for log file write failure.
  543. Entry pushlock should be acquired exclusive prior to calling this function.
  544. Arguments:
  545. pEntry - Log file entry
  546. Status - Result of last write
  547. --***************************************************************************/
  548. VOID
  549. UlpEventLogRawWriteFailure(
  550. IN PUL_BINARY_LOG_FILE_ENTRY pEntry,
  551. IN NTSTATUS Status
  552. )
  553. {
  554. NTSTATUS TempStatus = STATUS_SUCCESS;
  555. PWSTR StringList[1];
  556. //
  557. // Sanity Check.
  558. //
  559. PAGED_CODE();
  560. ASSERT(IS_VALID_BINARY_LOG_FILE_ENTRY(pEntry));
  561. //
  562. // There should better be a failure.
  563. //
  564. ASSERT(!NT_SUCCESS(Status));
  565. //
  566. // Bail out if we have already logged the event failure.
  567. //
  568. if (pEntry->Flags.WriteFailureLogged)
  569. {
  570. return;
  571. }
  572. //
  573. // Report the centralized binary log file name.
  574. //
  575. ASSERT(pEntry->pShortName);
  576. ASSERT(pEntry->pShortName[0] == L'\\');
  577. StringList[0] = (PWSTR) (pEntry->pShortName + 1); // Skip the L'\'
  578. TempStatus = UlWriteEventLogEntry(
  579. (NTSTATUS)EVENT_HTTP_LOGGING_BINARY_FILE_WRITE_FAILED,
  580. 0,
  581. 1,
  582. StringList,
  583. sizeof(NTSTATUS),
  584. (PVOID) &Status
  585. );
  586. ASSERT(TempStatus != STATUS_BUFFER_OVERFLOW);
  587. if (TempStatus == STATUS_SUCCESS)
  588. {
  589. pEntry->Flags.WriteFailureLogged = 1;
  590. }
  591. UlTrace(LOGGING,(
  592. "Http!UlpEventLogRawWriteFailure: Event Logging Status %08lx\n",
  593. TempStatus
  594. ));
  595. }
  596. /***************************************************************************++
  597. Routine Description:
  598. Simple wrapper function around global buffer flush.
  599. Arguments:
  600. pEntry - Binary Log file entry
  601. --***************************************************************************/
  602. NTSTATUS
  603. UlpFlushRawLogFile(
  604. IN PUL_BINARY_LOG_FILE_ENTRY pEntry
  605. )
  606. {
  607. NTSTATUS Status = STATUS_SUCCESS;
  608. ASSERT(IS_VALID_BINARY_LOG_FILE_ENTRY(pEntry));
  609. if (NULL != pEntry->LogBuffer && 0 != pEntry->LogBuffer->BufferUsed)
  610. {
  611. Status = UlFlushLogFileBuffer(
  612. &pEntry->LogBuffer,
  613. pEntry->pLogFile,
  614. (BOOLEAN)pEntry->Flags.HeaderFlushPending,
  615. &pEntry->TotalWritten.QuadPart
  616. );
  617. if (!NT_SUCCESS(Status))
  618. {
  619. UlpEventLogRawWriteFailure(pEntry, Status);
  620. }
  621. else
  622. {
  623. //
  624. // If we successfully flushed. Reset the event log indication.
  625. //
  626. pEntry->Flags.WriteFailureLogged = 0;
  627. }
  628. if (pEntry->Flags.HeaderFlushPending)
  629. {
  630. pEntry->Flags.HeaderFlushPending = 0;
  631. if (!NT_SUCCESS(Status))
  632. {
  633. //
  634. // We need to recopy the header, it couldn't make it
  635. // to the log file yet.
  636. //
  637. pEntry->Flags.HeaderWritten = 0;
  638. }
  639. }
  640. //
  641. // Buffer flush means activity, Reset the TimeToClose counter.
  642. //
  643. pEntry->TimeToClose = DEFAULT_MAX_FILE_IDLE_TIME;
  644. }
  645. return Status;
  646. }
  647. /***************************************************************************++
  648. Routine Description:
  649. Marks the entry inactive, closes the existing file.
  650. Caller should hold the log list eresource exclusive.
  651. Arguments:
  652. pEntry - The log file entry which we will mark inactive.
  653. --***************************************************************************/
  654. NTSTATUS
  655. UlpDisableBinaryEntry(
  656. IN OUT PUL_BINARY_LOG_FILE_ENTRY pEntry
  657. )
  658. {
  659. //
  660. // Sanity checks
  661. //
  662. PAGED_CODE();
  663. ASSERT(IS_VALID_BINARY_LOG_FILE_ENTRY(pEntry));
  664. UlTrace(BINARY_LOGGING,
  665. ("Http!UlpDisableBinaryEntry: pEntry %p disabled.\n",
  666. pEntry
  667. ));
  668. //
  669. // Flush and close the old file until the next recycle.
  670. //
  671. if (pEntry->pLogFile)
  672. {
  673. UlpFlushRawLogFile(pEntry);
  674. UlCloseLogFile(
  675. &pEntry->pLogFile
  676. );
  677. }
  678. //
  679. // Mark this inactive so that the next http hit awakens the entry.
  680. //
  681. pEntry->Flags.Active = 0;
  682. //
  683. // Init served cache for a new file.
  684. //
  685. InterlockedExchange((PLONG) &pEntry->ServedCacheHit, 0);
  686. //
  687. // Once we closed the old file, we have to traverse through
  688. // the Uri Cache and UNMARK all the IndexWritten flags.
  689. //
  690. UlClearCentralizedLogged(pEntry);
  691. return STATUS_SUCCESS;
  692. }
  693. /***************************************************************************++
  694. Routine Description:
  695. Small wrapper around handle recycle to ensure it happens under the system
  696. process context.
  697. Arguments:
  698. pEntry - Points to the existing entry.
  699. --***************************************************************************/
  700. NTSTATUS
  701. UlpRecycleBinaryLogFile(
  702. IN OUT PUL_BINARY_LOG_FILE_ENTRY pEntry
  703. )
  704. {
  705. NTSTATUS Status;
  706. PAGED_CODE();
  707. ASSERT(IS_VALID_BINARY_LOG_FILE_ENTRY(pEntry));
  708. Status = UlQueueLoggingRoutine(
  709. (PVOID) pEntry,
  710. &UlpHandleBinaryLogFileRecycle
  711. );
  712. return Status;
  713. }
  714. /***************************************************************************++
  715. Routine Description:
  716. This function requires to have the entry resource to be acquired.
  717. Sometimes it may be necessary to scan the new directory to figure out
  718. the correct sequence number and the file name. Especially after a dir
  719. name reconfig.
  720. Arguments:
  721. pEntry - Points to the binary log file entry
  722. --***************************************************************************/
  723. NTSTATUS
  724. UlpHandleBinaryLogFileRecycle(
  725. IN OUT PVOID pContext
  726. )
  727. {
  728. NTSTATUS Status;
  729. PUL_BINARY_LOG_FILE_ENTRY pEntry;
  730. TIME_FIELDS TimeFields;
  731. LARGE_INTEGER TimeStamp;
  732. PUL_LOG_FILE_HANDLE pLogFile;
  733. WCHAR _FileName[UL_MAX_FILE_NAME_SUFFIX_LENGTH + 1];
  734. UNICODE_STRING FileName;
  735. BOOLEAN UncShare;
  736. BOOLEAN ACLSupport;
  737. //
  738. // Sanity check.
  739. //
  740. PAGED_CODE();
  741. pEntry = (PUL_BINARY_LOG_FILE_ENTRY) pContext;
  742. ASSERT(IS_VALID_BINARY_LOG_FILE_ENTRY(pEntry));
  743. Status = STATUS_SUCCESS;
  744. pLogFile = NULL;
  745. FileName.Buffer = _FileName;
  746. FileName.Length = 0;
  747. FileName.MaximumLength = sizeof(_FileName);
  748. //
  749. // We have two criterions for the log file name
  750. // its LogFormat and its LogPeriod
  751. //
  752. ASSERT(pEntry->Period < HttpLoggingPeriodMaximum);
  753. ASSERT(pEntry->FileName.Length !=0 );
  754. UlTrace( BINARY_LOGGING,
  755. ("Http!UlpHandleBinaryLogFileRecycle: pEntry %p \n", pEntry ));
  756. //
  757. // This value is computed for the GMT time zone.
  758. //
  759. KeQuerySystemTime(&TimeStamp);
  760. RtlTimeToTimeFields(&TimeStamp, &TimeFields);
  761. //
  762. // If we need to scan the directory. Sequence number should start
  763. // from 1 again. Set this before constructing the log file name.
  764. //
  765. if (pEntry->Flags.StaleSequenceNumber &&
  766. pEntry->Period==HttpLoggingPeriodMaxSize)
  767. {
  768. //
  769. // Init otherwise if QueryDirectory doesn't find any files
  770. // in the provided directory, this will not get properly
  771. // initialized.
  772. //
  773. pEntry->SequenceNumber = 1;
  774. }
  775. //
  776. // Use binary logging settings when constructing the filename.
  777. //
  778. UlConstructFileName(
  779. pEntry->Period,
  780. BINARY_LOG_FILE_NAME_PREFIX,
  781. BINARY_LOG_FILE_NAME_EXTENSION,
  782. &FileName,
  783. &TimeFields,
  784. FALSE,
  785. &pEntry->SequenceNumber
  786. );
  787. if (pEntry->FileName.MaximumLength <= FileName.Length)
  788. {
  789. ASSERT(!"FileName buffer is not sufficient.");
  790. Status = STATUS_INVALID_PARAMETER;
  791. goto end;
  792. }
  793. //
  794. // Do the magic and renew the filename. Replace the old file
  795. // name with the new one.
  796. //
  797. ASSERT(pEntry->pShortName != NULL);
  798. //
  799. // Get rid of the old filename before scanning the
  800. // directories.
  801. //
  802. *((PWCHAR)pEntry->pShortName) = UNICODE_NULL;
  803. pEntry->FileName.Length =
  804. (USHORT) wcslen(pEntry->FileName.Buffer) * sizeof(WCHAR);
  805. //
  806. // Create/Open the director(ies) first. This might be
  807. // necessary if we get called after an entry reconfiguration
  808. // and directory name change or for the first time we
  809. // try to create/open the binary log file.
  810. //
  811. Status = UlCreateSafeDirectory(&pEntry->FileName,
  812. &UncShare,
  813. &ACLSupport
  814. );
  815. if (!NT_SUCCESS(Status))
  816. goto eventlog;
  817. //
  818. // Now Restore the short file name pointer back
  819. //
  820. pEntry->pShortName = (PWSTR)
  821. &(pEntry->FileName.Buffer[pEntry->FileName.Length/sizeof(WCHAR)]);
  822. //
  823. // Append the new file name ( based on the updated current time )
  824. // to the end.
  825. //
  826. Status = RtlAppendUnicodeStringToString( &pEntry->FileName, &FileName );
  827. if (!NT_SUCCESS(Status))
  828. goto end;
  829. //
  830. // Time to close the old file and reopen a new one
  831. //
  832. if (pEntry->pLogFile != NULL)
  833. {
  834. //
  835. // Flush,close and mark the entry inactive.
  836. //
  837. UlpDisableBinaryEntry(pEntry);
  838. }
  839. ASSERT(pEntry->pLogFile == NULL);
  840. //
  841. // If the sequence is stale because of the nature of the recycle.
  842. // And if our period is size based then rescan the new directory
  843. // to figure out the proper file to open.
  844. //
  845. pEntry->TotalWritten.QuadPart = (ULONGLONG) 0;
  846. if (pEntry->Flags.StaleSequenceNumber &&
  847. pEntry->Period==HttpLoggingPeriodMaxSize)
  848. {
  849. // This call may update the filename, the file size and the
  850. // sequence number if there is an old file in the new dir.
  851. Status = UlQueryDirectory(
  852. &pEntry->FileName,
  853. pEntry->pShortName,
  854. BINARY_LOG_FILE_NAME_PREFIX,
  855. BINARY_LOG_FILE_NAME_EXTENSION_PLUS_DOT,
  856. &pEntry->SequenceNumber,
  857. &pEntry->TotalWritten.QuadPart
  858. );
  859. if (!NT_SUCCESS(Status))
  860. {
  861. if (Status == STATUS_ACCESS_DENIED)
  862. {
  863. Status = STATUS_INVALID_OWNER;
  864. goto eventlog;
  865. }
  866. else
  867. {
  868. goto end;
  869. }
  870. }
  871. }
  872. //
  873. // Allocate a new log file structure for the new log file we are
  874. // about to open or create.
  875. //
  876. pLogFile = pEntry->pLogFile =
  877. UL_ALLOCATE_STRUCT(
  878. NonPagedPool,
  879. UL_LOG_FILE_HANDLE,
  880. UL_LOG_FILE_HANDLE_POOL_TAG
  881. );
  882. if (pLogFile == NULL)
  883. {
  884. Status = STATUS_NO_MEMORY;
  885. goto end;
  886. }
  887. pLogFile->Signature = UL_LOG_FILE_HANDLE_POOL_TAG;
  888. pLogFile->hFile = NULL;
  889. UlInitializeWorkItem(&pLogFile->WorkItem);
  890. //
  891. // Create the new log file.
  892. //
  893. Status = UlCreateLogFile(&pEntry->FileName,
  894. UncShare,
  895. ACLSupport,
  896. &pLogFile->hFile
  897. );
  898. if (!NT_SUCCESS(Status))
  899. {
  900. goto eventlog;
  901. }
  902. ASSERT(pLogFile->hFile);
  903. pEntry->TotalWritten.QuadPart = UlGetLogFileLength(pLogFile->hFile);
  904. //
  905. // Recalculate the time to expire.
  906. //
  907. if (pEntry->Flags.StaleTimeToExpire &&
  908. pEntry->Period != HttpLoggingPeriodMaxSize)
  909. {
  910. UlCalculateTimeToExpire(
  911. &TimeFields,
  912. pEntry->Period,
  913. &pEntry->TimeToExpire
  914. );
  915. }
  916. //
  917. // Set the time to close to default for a new file. The value is in
  918. // buffer flushup periods. Basically 15 minutes.
  919. //
  920. pEntry->TimeToClose = DEFAULT_MAX_FILE_IDLE_TIME;
  921. //
  922. // File is successfully opened and the entry is no longer inactive.
  923. // Update our state flags accordingly.
  924. //
  925. pEntry->Flags.Active = 1;
  926. pEntry->Flags.RecyclePending = 0;
  927. pEntry->Flags.StaleSequenceNumber = 0;
  928. pEntry->Flags.StaleTimeToExpire = 0;
  929. pEntry->Flags.HeaderWritten = 0;
  930. pEntry->Flags.CreateFileFailureLogged = 0;
  931. pEntry->Flags.WriteFailureLogged = 0;
  932. pEntry->Flags.HeaderFlushPending = 0;
  933. UlTrace(BINARY_LOGGING,
  934. ("Http!UlpHandleBinaryLogFileRecycle: entry %p, file %S, handle %lx\n",
  935. pEntry,
  936. pEntry->FileName.Buffer,
  937. pLogFile->hFile
  938. ));
  939. eventlog:
  940. if (!NT_SUCCESS(Status))
  941. {
  942. if (!pEntry->Flags.CreateFileFailureLogged)
  943. {
  944. NTSTATUS TempStatus;
  945. TempStatus = UlEventLogCreateFailure(
  946. Status,
  947. UlEventLogBinary,
  948. &pEntry->FileName,
  949. 0
  950. );
  951. if (TempStatus == STATUS_SUCCESS)
  952. {
  953. //
  954. // Avoid filling up the event log with error entries. This code
  955. // path might get hit every time a request arrives.
  956. //
  957. pEntry->Flags.CreateFileFailureLogged = 1;
  958. }
  959. UlTrace(LOGGING,(
  960. "Http!UlpHandleBinaryLogFileRecycle: Event Logging Status %08lx\n",
  961. TempStatus
  962. ));
  963. }
  964. }
  965. end:
  966. if (!NT_SUCCESS(Status))
  967. {
  968. UlTrace(BINARY_LOGGING,
  969. ("Http!UlpHandleBinaryLogFileRecycle: entry %p, failure %08lx\n",
  970. pEntry,
  971. Status
  972. ));
  973. if (pLogFile != NULL)
  974. {
  975. //
  976. // This means we have alread closed the old file but failed
  977. // when we try to create or open the new one.
  978. //
  979. ASSERT(pLogFile->hFile == NULL);
  980. UL_FREE_POOL_WITH_SIG(pLogFile,UL_LOG_FILE_HANDLE_POOL_TAG);
  981. pEntry->pLogFile = NULL;
  982. pEntry->Flags.Active = 0;
  983. }
  984. else
  985. {
  986. //
  987. // We were about to recyle the old one but something failed
  988. // lets try to flush and close the existing file if it's still
  989. // around.
  990. //
  991. if (pEntry->pLogFile)
  992. {
  993. UlpDisableBinaryEntry(pEntry);
  994. }
  995. }
  996. //
  997. // Mark this entry RecyclePending so that buffer timer can try to
  998. // resurrect this back every minute.
  999. //
  1000. pEntry->Flags.RecyclePending = 1;
  1001. }
  1002. return Status;
  1003. }
  1004. /***************************************************************************++
  1005. Routine Description:
  1006. Allocates a new log data buffer and captures the user log data into this
  1007. buffer in a format suitable for binary logging.
  1008. WARNING:
  1009. Even though the pLogFields is already captured to the kernel buffer, it
  1010. still holds pointers to user-mode memory for individual log fields,
  1011. therefore this function should be called inside a try/except block and
  1012. if exception happens, caller should cleanup the possibly allocated pLogData
  1013. structure (*ppLogData is always set when we allocate one)
  1014. Arguments:
  1015. pLogFields - User provided logging information, it will be used to build
  1016. some part of the binary logging record. It is already captured
  1017. to kernel buffer.
  1018. pRequest - Pointer to the currently logged request.
  1019. ppLogData - Returning the allocated pLogData.
  1020. --***************************************************************************/
  1021. NTSTATUS
  1022. UlCaptureRawLogData(
  1023. IN PHTTP_LOG_FIELDS_DATA pLogFields,
  1024. IN PUL_INTERNAL_REQUEST pRequest,
  1025. OUT PUL_LOG_DATA_BUFFER *ppLogData
  1026. )
  1027. {
  1028. PUL_LOG_DATA_BUFFER pLogData = NULL;
  1029. PUL_BINARY_LOG_DATA pBinaryData = NULL;
  1030. ULONG RawDataSize = 0;
  1031. USHORT UriStemSize = 0;
  1032. USHORT UriQuerySize = 0;
  1033. USHORT UserNameSize = 0;
  1034. PUCHAR pByte;
  1035. //
  1036. // Sanity check.
  1037. //
  1038. PAGED_CODE();
  1039. ASSERT(UL_IS_VALID_INTERNAL_REQUEST(pRequest));
  1040. *ppLogData = NULL;
  1041. //
  1042. // Calculate the required buffer size for the variable size strings.
  1043. // And either allocate from the lookaside list or from pool.
  1044. //
  1045. /* UriStem */
  1046. if (pLogFields->UriStemLength)
  1047. {
  1048. UriStemSize = MIN(pLogFields->UriStemLength,MAX_LOG_EXTEND_FIELD_LEN);
  1049. RawDataSize += UriStemSize;
  1050. }
  1051. /* UriQuery */
  1052. if (pLogFields->UriQueryLength)
  1053. {
  1054. UriQuerySize = MIN(pLogFields->UriQueryLength,MAX_LOG_EXTEND_FIELD_LEN);
  1055. RawDataSize += UriQuerySize;
  1056. }
  1057. /* UserName */
  1058. if (pLogFields->UserNameLength)
  1059. {
  1060. UserNameSize = MIN(pLogFields->UserNameLength,MAX_LOG_USERNAME_FIELD_LEN);
  1061. RawDataSize += UserNameSize;
  1062. }
  1063. if (RawDataSize > UL_BINARY_LOG_LINE_BUFFER_SIZE)
  1064. {
  1065. ASSERT(RawDataSize <=
  1066. (2 * MAX_LOG_EXTEND_FIELD_LEN + MAX_LOG_USERNAME_FIELD_LEN));
  1067. //
  1068. // Provided buffer is not big enough to hold the user data.
  1069. //
  1070. pLogData = UlReallocLogDataBuffer(RawDataSize,TRUE);
  1071. }
  1072. else
  1073. {
  1074. //
  1075. // Default id enough, try to pop it from the lookaside list.
  1076. //
  1077. pLogData = UlPplAllocateLogDataBuffer(TRUE);
  1078. }
  1079. //
  1080. // If failed to allocate then bail out. We won't be logging this request.
  1081. //
  1082. if (!pLogData)
  1083. {
  1084. return STATUS_INSUFFICIENT_RESOURCES;
  1085. }
  1086. ASSERT(pLogData->Flags.Binary == 1);
  1087. ASSERT(pLogData->Size > 0);
  1088. ASSERT(IS_VALID_LOG_DATA_BUFFER(pLogData));
  1089. pBinaryData = &pLogData->Data.Binary;
  1090. //
  1091. // Initialize necessary log fields in the Log Buffer
  1092. //
  1093. UL_REFERENCE_INTERNAL_REQUEST(pRequest);
  1094. pLogData->pRequest = pRequest;
  1095. *ppLogData = pLogData;
  1096. pLogData->Flags.CacheAndSendResponse = 0;
  1097. pLogData->BytesTransferred = 0;
  1098. UlInitializeWorkItem(&pLogData->WorkItem);
  1099. pLogData->Used = (USHORT) RawDataSize;
  1100. //
  1101. // Capture the fields from the user data.
  1102. //
  1103. pBinaryData->Method = pLogFields->MethodNum;
  1104. pBinaryData->Version =
  1105. (UCHAR) UlpHttpVersionToBinaryLogVersion(pRequest->Version);
  1106. pLogData->Win32Status = pLogFields->Win32Status;
  1107. pLogData->ProtocolStatus = pLogFields->ProtocolStatus;
  1108. pLogData->SubStatus = pLogFields->SubStatus;
  1109. pLogData->ServerPort = pLogFields->ServerPort;
  1110. //
  1111. // No indexing for cache-miss case.
  1112. //
  1113. pBinaryData->pUriStemID = NULL;
  1114. //
  1115. // Copy string fields to the kernel buffer.
  1116. //
  1117. pByte = pLogData->Line;
  1118. if (UriStemSize)
  1119. {
  1120. pBinaryData->pUriStem = pByte;
  1121. pBinaryData->UriStemSize = UriStemSize;
  1122. RtlCopyMemory(
  1123. pByte,
  1124. pLogFields->UriStem,
  1125. UriStemSize
  1126. );
  1127. pByte += UriStemSize;
  1128. }
  1129. else
  1130. {
  1131. pBinaryData->pUriStem = NULL;
  1132. pBinaryData->UriStemSize = 0;
  1133. }
  1134. if (UriQuerySize)
  1135. {
  1136. pBinaryData->pUriQuery = pByte;
  1137. pBinaryData->UriQuerySize = UriQuerySize;
  1138. RtlCopyMemory(
  1139. pByte,
  1140. pLogFields->UriQuery,
  1141. UriQuerySize
  1142. );
  1143. pByte += UriQuerySize;
  1144. }
  1145. else
  1146. {
  1147. pBinaryData->pUriQuery = NULL;
  1148. pBinaryData->UriQuerySize = 0;
  1149. }
  1150. if (UserNameSize)
  1151. {
  1152. pBinaryData->pUserName = pByte;
  1153. pBinaryData->UserNameSize = UserNameSize;
  1154. RtlCopyMemory(
  1155. pByte,
  1156. pLogFields->UserName,
  1157. UserNameSize
  1158. );
  1159. pByte += UserNameSize;
  1160. }
  1161. else
  1162. {
  1163. pBinaryData->pUserName = NULL;
  1164. pBinaryData->UserNameSize = 0;
  1165. }
  1166. UlTrace(BINARY_LOGGING,
  1167. ("Http!UlInitAndCaptureRawLogData: pLogData %p \n",pLogData));
  1168. return STATUS_SUCCESS;
  1169. }
  1170. /***************************************************************************++
  1171. Routine Description:
  1172. Copies the header to the binary log file.
  1173. Arguments:
  1174. pBuffer - Pointer to the file buffer. PVOID aligned.
  1175. --***************************************************************************/
  1176. ULONG
  1177. UlpRawCopyLogHeader(
  1178. IN PUCHAR pBuffer
  1179. )
  1180. {
  1181. PHTTP_RAW_FILE_HEADER pHeader = NULL;
  1182. PUCHAR pCurrentBufferPtr = NULL;
  1183. PAGED_CODE();
  1184. ASSERT(pBuffer);
  1185. UlTrace(BINARY_LOGGING,
  1186. ("Http!UlpRawCopyLogHeader: pBuffer %p\n", pBuffer ));
  1187. ASSERT(pBuffer == ALIGN_UP_POINTER(pBuffer,PVOID));
  1188. pCurrentBufferPtr = pBuffer;
  1189. pHeader = (PHTTP_RAW_FILE_HEADER) pBuffer;
  1190. pHeader->RecordType = HTTP_RAW_RECORD_HEADER_TYPE;
  1191. pHeader->MinorVersion = MINOR_RAW_LOG_FILE_VERSION;
  1192. pHeader->MajorVersion = MAJOR_RAW_LOG_FILE_VERSION;
  1193. pHeader->AlignmentSize = sizeof(PVOID);
  1194. KeQuerySystemTime( &pHeader->DateTime );
  1195. // TODO: BUGBUG need a KErnel API to get this similar to GetComputerNameA.
  1196. // TODO: Currently we read from registry.
  1197. wcsncpy(
  1198. pHeader->ComputerName,g_UlComputerName, (MAX_COMPUTER_NAME_LEN - 1));
  1199. pHeader->ComputerName[MAX_COMPUTER_NAME_LEN - 1] = UNICODE_NULL;
  1200. pCurrentBufferPtr += sizeof(HTTP_RAW_FILE_HEADER);
  1201. ASSERT(pCurrentBufferPtr ==
  1202. (PUCHAR) ALIGN_UP_POINTER(pCurrentBufferPtr, PVOID));
  1203. return DIFF(pCurrentBufferPtr-pBuffer);
  1204. }
  1205. /***************************************************************************++
  1206. Routine Description:
  1207. Copies the footer to the binary log file.
  1208. Arguments:
  1209. pBuffer - Pointer to the file buffer. PVOID aligned.
  1210. --***************************************************************************/
  1211. ULONG
  1212. UlpRawCopyLogFooter(
  1213. IN PUCHAR pBuffer
  1214. )
  1215. {
  1216. PHTTP_RAW_FILE_FOOTER pFooter = NULL;
  1217. PUCHAR pCurrentBufferPtr = NULL;
  1218. PAGED_CODE();
  1219. ASSERT(pBuffer);
  1220. UlTrace(BINARY_LOGGING,
  1221. ("Http!UlpRawCopyLogFooter: pBuffer %p\n", pBuffer ));
  1222. ASSERT(pBuffer == ALIGN_UP_POINTER(pBuffer,PVOID));
  1223. pCurrentBufferPtr = pBuffer;
  1224. pFooter = (PHTTP_RAW_FILE_FOOTER) pBuffer;
  1225. pFooter->RecordType = HTTP_RAW_RECORD_FOOTER_TYPE;
  1226. KeQuerySystemTime( &pFooter->DateTime );
  1227. pCurrentBufferPtr += sizeof(HTTP_RAW_FILE_HEADER);
  1228. ASSERT(pCurrentBufferPtr ==
  1229. (PUCHAR) ALIGN_UP_POINTER(pCurrentBufferPtr, PVOID));
  1230. return DIFF(pCurrentBufferPtr-pBuffer);
  1231. }
  1232. /***************************************************************************++
  1233. Routine Description:
  1234. Receives a pointer to the file buffer. ( IT MUST BE PVOID ALIGNED )
  1235. And copies the index records and the data record.
  1236. When copying the index records, string size is aligned up to PVOID. As a
  1237. result there MAY be padding characters between the index records and the
  1238. data record.
  1239. Arguments:
  1240. pContext - It should be a LogData pointer for cache-miss hits.
  1241. pBuffer - Pointer to the file buffer. PVOID aligned.
  1242. BytesRequired - Total number of bytes to be written.
  1243. --***************************************************************************/
  1244. VOID
  1245. UlpRawCopyForLogCacheMiss(
  1246. IN PVOID pContext,
  1247. IN PUCHAR pBuffer,
  1248. IN ULONG BytesRequired
  1249. )
  1250. {
  1251. PHTTP_RAW_FILE_MISS_LOG_DATA pRecord;
  1252. PUL_BINARY_LOG_DATA pBinaryData;
  1253. PUL_INTERNAL_REQUEST pRequest;
  1254. PUL_LOG_DATA_BUFFER pLogData;
  1255. PUL_CONNECTION pConnection;
  1256. PUCHAR pCurrentBufferPtr;
  1257. LONGLONG LifeTime;
  1258. LARGE_INTEGER CurrentTimeStamp;
  1259. ULONG IPAddressSize;
  1260. UNREFERENCED_PARAMETER(BytesRequired);
  1261. //
  1262. // Sanity Checks.
  1263. //
  1264. PAGED_CODE();
  1265. ASSERT(pContext);
  1266. ASSERT(pBuffer == (PUCHAR) ALIGN_UP_POINTER(pBuffer,PVOID));
  1267. ASSERT(BytesRequired);
  1268. pLogData = (PUL_LOG_DATA_BUFFER) pContext;
  1269. ASSERT(IS_VALID_LOG_DATA_BUFFER(pLogData));
  1270. UlTrace(BINARY_LOGGING,
  1271. ("Http!UlpRawCopyForLogCacheMiss: pLogData %p\n", pLogData ));
  1272. pBinaryData = &pLogData->Data.Binary;
  1273. pRequest = pLogData->pRequest;
  1274. ASSERT(UL_IS_VALID_INTERNAL_REQUEST(pRequest));
  1275. //
  1276. // Cast back the pointer to the file buffer to the record type
  1277. // pointer for cache-miss and fill in the fields in the record.
  1278. //
  1279. pCurrentBufferPtr = pBuffer;
  1280. pRecord = (PHTTP_RAW_FILE_MISS_LOG_DATA) pCurrentBufferPtr;
  1281. pRecord->RecordType = HTTP_RAW_RECORD_MISS_LOG_DATA_TYPE;
  1282. pRecord->Flags.Value = 0;
  1283. pRecord->Flags.Method = pBinaryData->Method;
  1284. pRecord->Flags.ProtocolVersion = pBinaryData->Version;
  1285. pRecord->SiteID = pRequest->ConfigInfo.SiteId;
  1286. KeQuerySystemTime(&CurrentTimeStamp);
  1287. pRecord->DateTime = CurrentTimeStamp;
  1288. pRecord->ServerPort = pLogData->ServerPort;
  1289. pRecord->ProtocolStatus = pLogData->ProtocolStatus;
  1290. pRecord->Win32Status = pLogData->Win32Status;
  1291. LifeTime = CurrentTimeStamp.QuadPart - pRequest->TimeStamp.QuadPart;
  1292. LifeTime = MAX(LifeTime,0); // Just in case system clock went backward
  1293. LifeTime /= C_NS_TICKS_PER_MSEC;
  1294. pRecord->TimeTaken = LifeTime;
  1295. pRecord->BytesSent = pRequest->BytesSent;
  1296. pRecord->BytesReceived = pRequest->BytesReceived;
  1297. pRecord->SubStatus = pLogData->SubStatus;
  1298. //
  1299. // Init the variable size field sizes.
  1300. //
  1301. pRecord->UriStemSize = pBinaryData->UriStemSize;
  1302. pRecord->UriQuerySize = pBinaryData->UriQuerySize;
  1303. pRecord->UserNameSize = pBinaryData->UserNameSize;
  1304. //
  1305. // Move forward to the end of the structure. It's better be PVOID
  1306. // aligned.
  1307. //
  1308. pCurrentBufferPtr += sizeof(HTTP_RAW_FILE_MISS_LOG_DATA);
  1309. ASSERT(pCurrentBufferPtr ==
  1310. (PUCHAR) ALIGN_UP_POINTER(pCurrentBufferPtr, PVOID));
  1311. //
  1312. // Now append the IP Addresses of the client and server
  1313. //
  1314. ASSERT(UL_IS_VALID_HTTP_CONNECTION(pRequest->pHttpConn));
  1315. pConnection = pRequest->pHttpConn->pConnection;
  1316. ASSERT(IS_VALID_CONNECTION(pConnection));
  1317. if ( pConnection->AddressType == TDI_ADDRESS_TYPE_IP)
  1318. {
  1319. PTDI_ADDRESS_IP pIPv4Address = &pConnection->RemoteAddrIn;
  1320. PHTTP_RAWLOG_IPV4_ADDRESSES pIPv4Buf =
  1321. (PHTTP_RAWLOG_IPV4_ADDRESSES) pCurrentBufferPtr;
  1322. ASSERT(pRecord->Flags.IPv6 == FALSE);
  1323. pIPv4Buf->Client = * (ULONG UNALIGNED *) &pIPv4Address->in_addr;
  1324. pIPv4Address = &pConnection->LocalAddrIn;
  1325. pIPv4Buf->Server = * (ULONG UNALIGNED *) &pIPv4Address->in_addr;
  1326. IPAddressSize = sizeof(HTTP_RAWLOG_IPV4_ADDRESSES);
  1327. }
  1328. else
  1329. {
  1330. PHTTP_RAWLOG_IPV6_ADDRESSES pIPv6Buf =
  1331. (PHTTP_RAWLOG_IPV6_ADDRESSES) pCurrentBufferPtr;
  1332. ASSERT(pConnection->AddressType == TDI_ADDRESS_TYPE_IP6);
  1333. pRecord->Flags.IPv6 = TRUE;
  1334. RtlCopyMemory(
  1335. pIPv6Buf->Client,
  1336. &pConnection->RemoteAddrIn6.sin6_addr,
  1337. sizeof(pIPv6Buf->Client)
  1338. );
  1339. RtlCopyMemory(
  1340. pIPv6Buf->Server,
  1341. &pConnection->LocalAddrIn6.sin6_addr,
  1342. sizeof(pIPv6Buf->Server)
  1343. );
  1344. IPAddressSize = sizeof(HTTP_RAWLOG_IPV6_ADDRESSES);
  1345. }
  1346. pCurrentBufferPtr += IPAddressSize;
  1347. ASSERT(pCurrentBufferPtr ==
  1348. (PUCHAR) ALIGN_UP_POINTER(pCurrentBufferPtr, PVOID));
  1349. //
  1350. // Now append the variable size fields to the end.
  1351. //
  1352. if (pBinaryData->UriStemSize)
  1353. {
  1354. ASSERT(pBinaryData->pUriStem);
  1355. ASSERT(!pBinaryData->pUriStemID);
  1356. RtlCopyMemory( pCurrentBufferPtr,
  1357. pBinaryData->pUriStem,
  1358. pBinaryData->UriStemSize
  1359. );
  1360. pCurrentBufferPtr += pBinaryData->UriStemSize;
  1361. }
  1362. if (pBinaryData->UriQuerySize)
  1363. {
  1364. ASSERT(pBinaryData->pUriQuery);
  1365. RtlCopyMemory( pCurrentBufferPtr,
  1366. pBinaryData->pUriQuery,
  1367. pBinaryData->UriQuerySize
  1368. );
  1369. pCurrentBufferPtr += pBinaryData->UriQuerySize;
  1370. }
  1371. if (pBinaryData->UserNameSize)
  1372. {
  1373. ASSERT(pBinaryData->pUserName);
  1374. RtlCopyMemory( pCurrentBufferPtr,
  1375. pBinaryData->pUserName,
  1376. pBinaryData->UserNameSize
  1377. );
  1378. pCurrentBufferPtr += pBinaryData->UserNameSize;
  1379. }
  1380. //
  1381. // Ensure that we still have the PVOID alignment in place.
  1382. //
  1383. pCurrentBufferPtr =
  1384. (PUCHAR) ALIGN_UP_POINTER(pCurrentBufferPtr, PVOID);
  1385. ASSERT(BytesRequired == DIFF(pCurrentBufferPtr-pBuffer));
  1386. //
  1387. // Well done. Good to go !
  1388. //
  1389. return;
  1390. }
  1391. /***************************************************************************++
  1392. Routine Description:
  1393. It does the binary logging for the cache-miss case.
  1394. Arguments:
  1395. pLogData - This should be a binary log data buffer.
  1396. --***************************************************************************/
  1397. NTSTATUS
  1398. UlRawLogHttpHit(
  1399. IN PUL_LOG_DATA_BUFFER pLogData
  1400. )
  1401. {
  1402. NTSTATUS Status = STATUS_SUCCESS;
  1403. PUL_CONTROL_CHANNEL pControlChannel;
  1404. PUL_INTERNAL_REQUEST pRequest;
  1405. PUL_BINARY_LOG_FILE_ENTRY pEntry;
  1406. ULONG BytesRequired = 0;
  1407. PUL_BINARY_LOG_DATA pBinaryData;
  1408. ULONG IPAddressSize;
  1409. ULONG VarFieldSize;
  1410. PUL_CONNECTION pConnection;
  1411. //
  1412. // Sanity checks.
  1413. //
  1414. PAGED_CODE();
  1415. UlTrace(BINARY_LOGGING,
  1416. ("Http!UlRawLogHttpHit: pLogData %p\n", pLogData ));
  1417. ASSERT(IS_VALID_LOG_DATA_BUFFER(pLogData));
  1418. pBinaryData = &pLogData->Data.Binary;
  1419. pRequest = pLogData->pRequest;
  1420. ASSERT(UL_IS_VALID_INTERNAL_REQUEST(pRequest));
  1421. ASSERT(UlBinaryLoggingEnabled(pRequest->ConfigInfo.pControlChannel));
  1422. pControlChannel = pRequest->ConfigInfo.pControlChannel;
  1423. ASSERT(IS_VALID_CONTROL_CHANNEL(pControlChannel));
  1424. pEntry = pControlChannel->pBinaryLogEntry;
  1425. ASSERT(IS_VALID_BINARY_LOG_FILE_ENTRY(pEntry));
  1426. ASSERT(UL_IS_VALID_HTTP_CONNECTION(pRequest->pHttpConn));
  1427. pConnection = pRequest->pHttpConn->pConnection;
  1428. ASSERT(IS_VALID_CONNECTION(pConnection));
  1429. //
  1430. // See how much space we need first.
  1431. //
  1432. if(pConnection->AddressType == TDI_ADDRESS_TYPE_IP)
  1433. {
  1434. IPAddressSize = sizeof(HTTP_RAWLOG_IPV4_ADDRESSES);
  1435. }
  1436. else if(pConnection->AddressType == TDI_ADDRESS_TYPE_IP6)
  1437. {
  1438. IPAddressSize = sizeof(HTTP_RAWLOG_IPV6_ADDRESSES);
  1439. }
  1440. else
  1441. {
  1442. ASSERT(!"Unknown IP Address Type !");
  1443. return STATUS_NOT_SUPPORTED;
  1444. }
  1445. ASSERT(IPAddressSize == ALIGN_UP(IPAddressSize, PVOID));
  1446. VarFieldSize = ALIGN_UP((pBinaryData->UriStemSize
  1447. + pBinaryData->UriQuerySize
  1448. + pBinaryData->UserNameSize), PVOID);
  1449. BytesRequired = sizeof(HTTP_RAW_FILE_MISS_LOG_DATA)
  1450. + IPAddressSize
  1451. + VarFieldSize
  1452. ;
  1453. ASSERT(BytesRequired == ALIGN_UP(BytesRequired, PVOID));
  1454. //
  1455. // Open the binary log file if necessary.
  1456. //
  1457. Status = UlpCheckRawFile( pEntry, pControlChannel );
  1458. if (NT_SUCCESS(Status))
  1459. {
  1460. //
  1461. // Now we know that the log file is there,
  1462. // it's time to write.
  1463. //
  1464. Status =
  1465. UlpWriteToRawLogFile (
  1466. pEntry,
  1467. NULL,
  1468. BytesRequired,
  1469. &UlpRawCopyForLogCacheMiss,
  1470. pLogData
  1471. );
  1472. }
  1473. return Status;
  1474. }
  1475. /***************************************************************************++
  1476. Routine Description:
  1477. Receives a pointer to the file buffer. ( IT MUST BE PVOID ALIGNED )
  1478. And copies the index records and the data record.
  1479. When copying the index records, string size is aligned up to PVOID. As a
  1480. result there MAY be padding characters between the index records and the
  1481. data record.
  1482. Arguments:
  1483. pContext - It should be a tracker pointer for cache hits.
  1484. pBuffer - Pointer to the file buffer. PVOID aligned.
  1485. BytesRequired - Total number of bytes to be written.
  1486. --***************************************************************************/
  1487. VOID
  1488. UlpRawCopyForLogCacheHit(
  1489. IN PVOID pContext,
  1490. IN PUCHAR pBuffer,
  1491. IN ULONG BytesRequired
  1492. )
  1493. {
  1494. PHTTP_RAW_INDEX_FIELD_DATA pIndex;
  1495. PHTTP_RAW_FILE_HIT_LOG_DATA pRecord;
  1496. PUL_INTERNAL_REQUEST pRequest;
  1497. PUCHAR pCurrentBufferPtr;
  1498. LONGLONG LifeTime;
  1499. LARGE_INTEGER CurrentTimeStamp;
  1500. PUL_URI_CACHE_ENTRY pUriCacheEntry;
  1501. PUL_FULL_TRACKER pTracker;
  1502. PUL_CONNECTION pConnection;
  1503. ULONG IPAddressSize;
  1504. //
  1505. // Sanity checks
  1506. //
  1507. PAGED_CODE();
  1508. ASSERT(pContext);
  1509. ASSERT(pBuffer == (PUCHAR) ALIGN_UP_POINTER(pBuffer,PVOID));
  1510. ASSERT(BytesRequired);
  1511. pTracker = (PUL_FULL_TRACKER) pContext;
  1512. ASSERT(IS_VALID_FULL_TRACKER(pTracker));
  1513. UlTrace(BINARY_LOGGING,
  1514. ("Http!UlpRawCopyForLogCacheHit: pTracker %p\n", pTracker ));
  1515. pRequest = pTracker->pRequest;
  1516. ASSERT(UL_IS_VALID_INTERNAL_REQUEST(pRequest));
  1517. pUriCacheEntry = pTracker->pUriEntry;
  1518. ASSERT( IS_VALID_URI_CACHE_ENTRY(pUriCacheEntry) );
  1519. ASSERT(pUriCacheEntry->pLogData);
  1520. ASSERT(pUriCacheEntry->LogDataLength == sizeof(HTTP_RAWLOGID));
  1521. ASSERT(UL_IS_VALID_HTTP_CONNECTION(pRequest->pHttpConn));
  1522. pConnection = pRequest->pHttpConn->pConnection;
  1523. ASSERT(IS_VALID_CONNECTION(pConnection));
  1524. if(pConnection->AddressType == TDI_ADDRESS_TYPE_IP)
  1525. {
  1526. IPAddressSize = sizeof(HTTP_RAWLOG_IPV4_ADDRESSES);
  1527. }
  1528. else
  1529. {
  1530. ASSERT(pConnection->AddressType == TDI_ADDRESS_TYPE_IP6);
  1531. IPAddressSize = sizeof(HTTP_RAWLOG_IPV6_ADDRESSES);
  1532. }
  1533. //
  1534. // For cache hits, UriQuery & UserName will be NULL.
  1535. // We will only be dealing with the UriStem field.
  1536. //
  1537. pCurrentBufferPtr = pBuffer;
  1538. if (BytesRequired > (sizeof(HTTP_RAW_FILE_HIT_LOG_DATA) + IPAddressSize))
  1539. {
  1540. PWSTR pUri;
  1541. ULONG UriSize;
  1542. //
  1543. // First time, we have to write the index.
  1544. //
  1545. ASSERT(pUriCacheEntry->UriKey.pUri);
  1546. ASSERT(pUriCacheEntry->UriKey.Length);
  1547. pUri = URI_FROM_CACHE(pUriCacheEntry->UriKey);
  1548. UriSize = URI_SIZE_FROM_CACHE(pUriCacheEntry->UriKey);
  1549. UlTrace(BINARY_LOGGING,
  1550. ("Http!UlpRawCopyForLogCacheHit: pUri %S UriSize %d\n",
  1551. pUri,
  1552. UriSize
  1553. ));
  1554. pIndex = (PHTTP_RAW_INDEX_FIELD_DATA) pCurrentBufferPtr;
  1555. pIndex->RecordType = HTTP_RAW_RECORD_INDEX_DATA_TYPE;
  1556. pIndex->Size = (USHORT) UriSize; // In Bytes
  1557. RtlCopyMemory(pIndex->Str, pUri, UriSize);
  1558. //
  1559. // The Uri is cached. Will use the provided Id from entry.
  1560. // Carefull with the pLogData in the cache entry, it could
  1561. // be unaligned.
  1562. //
  1563. RtlCopyMemory(&pIndex->Id,
  1564. pUriCacheEntry->pLogData,
  1565. sizeof(HTTP_RAWLOGID)
  1566. );
  1567. pCurrentBufferPtr += sizeof(HTTP_RAW_INDEX_FIELD_DATA);
  1568. //
  1569. // Carefully adjust the alignment. If the uri was smaller
  1570. // than 4 bytes it was inlined anyway.
  1571. //
  1572. if (UriSize > URI_BYTES_INLINED)
  1573. {
  1574. pCurrentBufferPtr +=
  1575. ALIGN_UP((UriSize - URI_BYTES_INLINED),PVOID);
  1576. }
  1577. }
  1578. ASSERT(pCurrentBufferPtr ==
  1579. (PUCHAR) ALIGN_UP_POINTER(pCurrentBufferPtr, PVOID));
  1580. //
  1581. // Now fill in the data record itself.
  1582. //
  1583. pRecord = (PHTTP_RAW_FILE_HIT_LOG_DATA) pCurrentBufferPtr;
  1584. pRecord->RecordType = HTTP_RAW_RECORD_HIT_LOG_DATA_TYPE;
  1585. pRecord->Flags.Value = 0;
  1586. pRecord->Flags.Method = pUriCacheEntry->Verb;
  1587. pRecord->Flags.ProtocolVersion =
  1588. (UCHAR) UlpHttpVersionToBinaryLogVersion(pRequest->Version);
  1589. pRecord->SiteID = pUriCacheEntry->ConfigInfo.SiteId;
  1590. KeQuerySystemTime(&CurrentTimeStamp);
  1591. pRecord->DateTime = CurrentTimeStamp;
  1592. // ServerPort will be copied later, down below.
  1593. pRecord->ProtocolStatus = pUriCacheEntry->StatusCode;
  1594. pRecord->Win32Status =
  1595. HttpNtStatusToWin32Status(pTracker->IoStatus.Status);
  1596. LifeTime = CurrentTimeStamp.QuadPart - pRequest->TimeStamp.QuadPart;
  1597. LifeTime = MAX(LifeTime,0); // Just in case system clock went backward
  1598. LifeTime /= C_NS_TICKS_PER_MSEC;
  1599. pRecord->TimeTaken = LifeTime;
  1600. pRecord->BytesSent = pTracker->IoStatus.Information;
  1601. pRecord->BytesReceived = pRequest->BytesReceived;
  1602. RtlCopyMemory(&pRecord->UriStemId,
  1603. pUriCacheEntry->pLogData,
  1604. sizeof(HTTP_RAWLOGID)
  1605. );
  1606. //
  1607. // Completed the fixed length portion of log record. Move to the
  1608. // end.
  1609. //
  1610. pCurrentBufferPtr += sizeof(HTTP_RAW_FILE_HIT_LOG_DATA);
  1611. ASSERT(pCurrentBufferPtr ==
  1612. (PUCHAR) ALIGN_UP_POINTER(pCurrentBufferPtr, PVOID));
  1613. //
  1614. // Now append the IP Addresses of the client and server
  1615. //
  1616. if ( pConnection->AddressType == TDI_ADDRESS_TYPE_IP)
  1617. {
  1618. PTDI_ADDRESS_IP pIPv4Address = &pConnection->RemoteAddrIn;
  1619. PHTTP_RAWLOG_IPV4_ADDRESSES pIPv4Buf =
  1620. (PHTTP_RAWLOG_IPV4_ADDRESSES) pCurrentBufferPtr;
  1621. ASSERT(pRecord->Flags.IPv6 == FALSE);
  1622. pIPv4Buf->Client = * (ULONG UNALIGNED *) &pIPv4Address->in_addr;
  1623. pIPv4Address = &pConnection->LocalAddrIn;
  1624. pIPv4Buf->Server = * (ULONG UNALIGNED *) &pIPv4Address->in_addr;
  1625. IPAddressSize = sizeof(HTTP_RAWLOG_IPV4_ADDRESSES);
  1626. //
  1627. // Init the ServerPort frm LocalAddrIn.
  1628. //
  1629. pRecord->ServerPort = SWAP_SHORT(pConnection->LocalAddrIn.sin_port);
  1630. }
  1631. else
  1632. {
  1633. PHTTP_RAWLOG_IPV6_ADDRESSES pIPv6Buf =
  1634. (PHTTP_RAWLOG_IPV6_ADDRESSES) pCurrentBufferPtr;
  1635. ASSERT(pConnection->AddressType == TDI_ADDRESS_TYPE_IP6);
  1636. pRecord->Flags.IPv6 = TRUE;
  1637. RtlCopyMemory(
  1638. pIPv6Buf->Client,
  1639. &pConnection->RemoteAddrIn6.sin6_addr,
  1640. sizeof(pIPv6Buf->Client)
  1641. );
  1642. RtlCopyMemory(
  1643. pIPv6Buf->Server,
  1644. &pConnection->LocalAddrIn6.sin6_addr,
  1645. sizeof(pIPv6Buf->Server)
  1646. );
  1647. IPAddressSize = sizeof(HTTP_RAWLOG_IPV6_ADDRESSES);
  1648. //
  1649. // Init the ServerPort from LocalAddrIn.
  1650. //
  1651. pRecord->ServerPort = SWAP_SHORT(pConnection->LocalAddrIn6.sin6_port);
  1652. }
  1653. //
  1654. // Some post sanity check to ensure that we still have the
  1655. // PVOID alignment in place.
  1656. //
  1657. pCurrentBufferPtr += IPAddressSize;
  1658. ASSERT(pCurrentBufferPtr ==
  1659. (PUCHAR) ALIGN_UP_POINTER(pCurrentBufferPtr, PVOID));
  1660. ASSERT(BytesRequired == DIFF(pCurrentBufferPtr-pBuffer));
  1661. //
  1662. // Well done. Good to go !
  1663. //
  1664. return;
  1665. }
  1666. /***************************************************************************++
  1667. Routine Description:
  1668. Handles binary logging for the cache hits.
  1669. Arguments:
  1670. pTracker - Supplies the full tracker.
  1671. --***************************************************************************/
  1672. NTSTATUS
  1673. UlRawLogHttpCacheHit(
  1674. IN PUL_FULL_TRACKER pTracker
  1675. )
  1676. {
  1677. NTSTATUS Status = STATUS_SUCCESS;
  1678. PUL_CONTROL_CHANNEL pControlChannel;
  1679. PUL_BINARY_LOG_FILE_ENTRY pEntry;
  1680. PUL_URI_CACHE_ENTRY pUriEntry;
  1681. ULONG BytesRequired;
  1682. PUL_CONNECTION pConnection;
  1683. ULONG IPAddressSize;
  1684. //
  1685. // Sanity checks.
  1686. //
  1687. PAGED_CODE();
  1688. ASSERT(pTracker);
  1689. ASSERT(IS_VALID_FULL_TRACKER(pTracker));
  1690. UlTrace(BINARY_LOGGING,
  1691. ("Http!UlRawLogHttpCacheHit: pTracker %p\n",
  1692. pTracker ));
  1693. pUriEntry = pTracker->pUriEntry;
  1694. ASSERT(IS_VALID_URI_CACHE_ENTRY(pUriEntry));
  1695. pControlChannel = pUriEntry->ConfigInfo.pControlChannel;
  1696. ASSERT(IS_VALID_CONTROL_CHANNEL(pControlChannel));
  1697. pEntry = pControlChannel->pBinaryLogEntry;
  1698. ASSERT(IS_VALID_BINARY_LOG_FILE_ENTRY(pEntry));
  1699. ASSERT(UlBinaryLoggingEnabled(pControlChannel));
  1700. ASSERT(UL_IS_VALID_INTERNAL_REQUEST(pTracker->pRequest));
  1701. ASSERT(UL_IS_VALID_HTTP_CONNECTION(pTracker->pRequest->pHttpConn));
  1702. pConnection = pTracker->pRequest->pHttpConn->pConnection;
  1703. ASSERT(IS_VALID_CONNECTION(pConnection));
  1704. //
  1705. // See how much space we need first. This will be incremented for
  1706. // a possible index record which me might add. However for that
  1707. // calculation we need to be inside the entry pushlock, that's
  1708. // why it is done by the function UlpWriteToRawLogFile down below.
  1709. //
  1710. if(pConnection->AddressType == TDI_ADDRESS_TYPE_IP)
  1711. {
  1712. IPAddressSize = sizeof(HTTP_RAWLOG_IPV4_ADDRESSES);
  1713. }
  1714. else if(pConnection->AddressType == TDI_ADDRESS_TYPE_IP6)
  1715. {
  1716. IPAddressSize = sizeof(HTTP_RAWLOG_IPV6_ADDRESSES);
  1717. }
  1718. else
  1719. {
  1720. ASSERT(!"Unknown IP Address Type !");
  1721. Status = STATUS_NOT_SUPPORTED;
  1722. goto end;
  1723. }
  1724. ASSERT(IPAddressSize == ALIGN_UP(IPAddressSize, PVOID));
  1725. BytesRequired = sizeof(HTTP_RAW_FILE_HIT_LOG_DATA) + IPAddressSize;
  1726. ASSERT(BytesRequired == ALIGN_UP(BytesRequired, PVOID));
  1727. //
  1728. // Open the binary log file if necessary.
  1729. //
  1730. Status = UlpCheckRawFile(pEntry, pControlChannel);
  1731. if (NT_SUCCESS(Status))
  1732. {
  1733. //
  1734. // Now we know that the log file is there,
  1735. // it's time to write.
  1736. //
  1737. Status =
  1738. UlpWriteToRawLogFile (
  1739. pEntry,
  1740. pUriEntry,
  1741. BytesRequired,
  1742. &UlpRawCopyForLogCacheHit,
  1743. pTracker
  1744. );
  1745. if (NT_SUCCESS(Status))
  1746. {
  1747. //
  1748. // Mark that we have successfully served a cache entry.
  1749. //
  1750. InterlockedExchange((PLONG) &pEntry->ServedCacheHit, 1);
  1751. }
  1752. }
  1753. end:
  1754. //
  1755. // If this was a build&send cache hit, then the tracker will
  1756. // still have the originally allocated pLogData. We don't use
  1757. // it here, nevertheless we will do the cleanup.
  1758. //
  1759. if (pTracker->pLogData)
  1760. {
  1761. ASSERT(IS_VALID_LOG_DATA_BUFFER(pTracker->pLogData));
  1762. UlDestroyLogDataBuffer(pTracker->pLogData);
  1763. pTracker->pLogData = NULL;
  1764. }
  1765. return Status;
  1766. }
  1767. /***************************************************************************++
  1768. Routine Description:
  1769. Exclusive (Debug) writer function.
  1770. REQUIRES you to hold the binary entry lock EXCLUSIVE.
  1771. Arguments:
  1772. pEntry - The binary log file entry we are working on.
  1773. BytesRequired - The amount (in bytes) of data will be written.
  1774. pBufferWritter - Caller provided writer function.
  1775. pContext - Necessary context for the writer function.
  1776. --***************************************************************************/
  1777. NTSTATUS
  1778. UlpWriteToRawLogFileDebug(
  1779. IN PUL_BINARY_LOG_FILE_ENTRY pEntry,
  1780. IN ULONG BytesRequired,
  1781. IN PUL_RAW_LOG_COPIER pBufferWritter,
  1782. IN PVOID pContext,
  1783. OUT PLONG pBinaryIndexWritten
  1784. )
  1785. {
  1786. PUL_LOG_FILE_BUFFER pLogBuffer;
  1787. NTSTATUS Status = STATUS_SUCCESS;
  1788. ULONG BytesRequiredPlusHeader = BytesRequired;
  1789. PAGED_CODE();
  1790. ASSERT(pContext);
  1791. ASSERT(pBufferWritter);
  1792. ASSERT(BytesRequired);
  1793. ASSERT(IS_VALID_BINARY_LOG_FILE_ENTRY(pEntry));
  1794. UlTrace(BINARY_LOGGING,
  1795. ("Http!UlpWriteToRawLogFileDebug: pEntry %p\n", pEntry));
  1796. ASSERT(UlDbgPushLockOwnedExclusive(&pEntry->PushLock));
  1797. ASSERT(g_UlDisableLogBuffering != 0);
  1798. //
  1799. // First append title to the temp buffer to calculate the size of
  1800. // the title if we need to write the title as well.
  1801. //
  1802. if (!pEntry->Flags.HeaderWritten)
  1803. {
  1804. BytesRequiredPlusHeader += sizeof(HTTP_RAW_FILE_HEADER);
  1805. }
  1806. if (BytesRequiredPlusHeader > g_UlLogBufferSize)
  1807. {
  1808. ASSERT(!"Record Size is too big !");
  1809. return STATUS_INVALID_PARAMETER;
  1810. }
  1811. //
  1812. // Now check the log file for overflow.
  1813. //
  1814. if (UlpIsRawLogFileOverFlow(pEntry, BytesRequiredPlusHeader))
  1815. {
  1816. Status = UlpRecycleBinaryLogFile(pEntry);
  1817. }
  1818. if (pEntry->pLogFile == NULL || !NT_SUCCESS(Status))
  1819. {
  1820. //
  1821. // If somehow the logging ceased and handle released,
  1822. // It happens when the recycle isn't able to write to
  1823. // the log drive.
  1824. //
  1825. return Status;
  1826. }
  1827. //
  1828. // The pLogBuffer may not be null, if previously a cache
  1829. // flush entry has been written.
  1830. //
  1831. pLogBuffer = pEntry->LogBuffer;
  1832. if (pLogBuffer == NULL)
  1833. {
  1834. pLogBuffer = pEntry->LogBuffer = UlPplAllocateLogFileBuffer();
  1835. if (!pLogBuffer)
  1836. {
  1837. return STATUS_NO_MEMORY;
  1838. }
  1839. }
  1840. //
  1841. // Very first hit needs to write the title, as well as a hit
  1842. // which causes the log file recycling.
  1843. //
  1844. if (!pEntry->Flags.HeaderWritten)
  1845. {
  1846. ULONG BytesCopied =
  1847. UlpRawCopyLogHeader(
  1848. pLogBuffer->Buffer + pLogBuffer->BufferUsed
  1849. );
  1850. pLogBuffer->BufferUsed += BytesCopied;
  1851. ASSERT(BytesCopied == sizeof(HTTP_RAW_FILE_HEADER));
  1852. pEntry->Flags.HeaderWritten = 1;
  1853. pEntry->Flags.HeaderFlushPending = 1;
  1854. }
  1855. pBufferWritter(
  1856. pContext,
  1857. pLogBuffer->Buffer + pLogBuffer->BufferUsed,
  1858. BytesRequired
  1859. );
  1860. pLogBuffer->BufferUsed += BytesRequired;
  1861. if (pBinaryIndexWritten &&
  1862. 0 == pEntry->Flags.CacheFlushInProgress)
  1863. {
  1864. InterlockedExchange(pBinaryIndexWritten, 1);
  1865. }
  1866. //
  1867. // Now flush what we have.
  1868. //
  1869. Status = UlpFlushRawLogFile(pEntry);
  1870. if (!NT_SUCCESS(Status))
  1871. {
  1872. return Status;
  1873. }
  1874. return STATUS_SUCCESS;
  1875. }
  1876. /***************************************************************************++
  1877. Routine Description:
  1878. It tries to write to the file buffer with shared lock.
  1879. Exits and returns STATUS_MORE_PROCESSING_REQUIRED for exclusive access
  1880. for the following conditions;
  1881. 1. No log buffer available.
  1882. 2. Logging ceased. (NULL file handle)
  1883. 3. Header needs to be written.
  1884. 4. Recycle is necessary because of a size overflow.
  1885. 5. No available space left in the current buffer.
  1886. Need to allocate a new one.
  1887. Otherwise reserves a space in the current buffer, copies the data by
  1888. calling the provided writer function.
  1889. Arguments:
  1890. pEntry - The binary log file entry we are working on.
  1891. BytesRequired - The amount (in bytes) of data will be written.
  1892. pBufferWritter - Caller provided writer function.
  1893. pContext - Necessary context for the writer function.
  1894. --***************************************************************************/
  1895. NTSTATUS
  1896. UlpWriteToRawLogFileShared(
  1897. IN PUL_BINARY_LOG_FILE_ENTRY pEntry,
  1898. IN ULONG BytesRequired,
  1899. IN PUL_RAW_LOG_COPIER pBufferWritter,
  1900. IN PVOID pContext,
  1901. OUT PLONG pBinaryIndexWritten
  1902. )
  1903. {
  1904. PUL_LOG_FILE_BUFFER pLogBuffer;
  1905. LONG BufferUsed;
  1906. PAGED_CODE();
  1907. ASSERT(pContext);
  1908. ASSERT(pBufferWritter);
  1909. ASSERT(BytesRequired);
  1910. ASSERT(IS_VALID_BINARY_LOG_FILE_ENTRY(pEntry));
  1911. pLogBuffer = pEntry->LogBuffer;
  1912. UlTrace(BINARY_LOGGING,
  1913. ("Http!UlpWriteToLogRawFileShared: pEntry %p\n", pEntry));
  1914. //
  1915. // Bail out and try the exclusive writer.
  1916. //
  1917. if ( pLogBuffer == NULL ||
  1918. pEntry->pLogFile == NULL ||
  1919. !pEntry->Flags.HeaderWritten ||
  1920. UlpIsRawLogFileOverFlow(pEntry,BytesRequired)
  1921. )
  1922. {
  1923. return STATUS_MORE_PROCESSING_REQUIRED;
  1924. }
  1925. //
  1926. // Reserve space in pLogBuffer by InterlockedCompareExchange add
  1927. // RecordSize. If we exceed the limit, bail out and take the
  1928. // exclusive lock to flush the buffer.
  1929. //
  1930. do
  1931. {
  1932. BufferUsed = *((volatile LONG *) &pLogBuffer->BufferUsed);
  1933. if ( BytesRequired + BufferUsed > g_UlLogBufferSize )
  1934. {
  1935. return STATUS_MORE_PROCESSING_REQUIRED;
  1936. }
  1937. PAUSE_PROCESSOR;
  1938. } while (BufferUsed != InterlockedCompareExchange(
  1939. &pLogBuffer->BufferUsed,
  1940. BytesRequired + BufferUsed,
  1941. BufferUsed
  1942. ));
  1943. //
  1944. // Now we have a reserved space lets proceed with the copying.
  1945. //
  1946. pBufferWritter(
  1947. pContext,
  1948. pLogBuffer->Buffer + BufferUsed,
  1949. BytesRequired
  1950. );
  1951. if (pBinaryIndexWritten &&
  1952. 0 == pEntry->Flags.CacheFlushInProgress)
  1953. {
  1954. InterlockedExchange(pBinaryIndexWritten, 1);
  1955. }
  1956. return STATUS_SUCCESS;
  1957. }
  1958. /***************************************************************************++
  1959. Routine Description:
  1960. Exclusive writer counterpart of the above function..
  1961. Arguments:
  1962. pEntry - The binary log file entry we are working on.
  1963. BytesRequired - The amount (in bytes) of data will be written.
  1964. pBufferWritter - Caller provided writer function.
  1965. pContext - Necessary context for the writer function.
  1966. --***************************************************************************/
  1967. NTSTATUS
  1968. UlpWriteToRawLogFileExclusive(
  1969. IN PUL_BINARY_LOG_FILE_ENTRY pEntry,
  1970. IN ULONG BytesRequired,
  1971. IN PUL_RAW_LOG_COPIER pBufferWritter,
  1972. IN PVOID pContext,
  1973. OUT PLONG pBinaryIndexWritten
  1974. )
  1975. {
  1976. PUL_LOG_FILE_BUFFER pLogBuffer;
  1977. NTSTATUS Status = STATUS_SUCCESS;
  1978. ULONG BytesRequiredPlusHeader = BytesRequired;
  1979. PAGED_CODE();
  1980. ASSERT(pContext);
  1981. ASSERT(pBufferWritter);
  1982. ASSERT(BytesRequired);
  1983. ASSERT(IS_VALID_BINARY_LOG_FILE_ENTRY(pEntry));
  1984. UlTrace(BINARY_LOGGING,
  1985. ("Http!UlpWriteToRawLogFileExclusive: pEntry %p\n", pEntry));
  1986. ASSERT(UlDbgPushLockOwnedExclusive(&pEntry->PushLock));
  1987. //
  1988. // First append title to the temp buffer to calculate the size of
  1989. // the title if we need to write the title as well.
  1990. //
  1991. if (!pEntry->Flags.HeaderWritten)
  1992. {
  1993. BytesRequiredPlusHeader += sizeof(HTTP_RAW_FILE_HEADER);
  1994. }
  1995. if (BytesRequiredPlusHeader > g_UlLogBufferSize)
  1996. {
  1997. ASSERT(!"Record Size is too big !");
  1998. return STATUS_INVALID_PARAMETER;
  1999. }
  2000. //
  2001. // Now check the log file for overflow.
  2002. //
  2003. if (UlpIsRawLogFileOverFlow(pEntry,BytesRequiredPlusHeader))
  2004. {
  2005. Status = UlpRecycleBinaryLogFile(pEntry);
  2006. }
  2007. // TODO: We should perhaps try to awaken the entry if it the entry state is
  2008. // TODO: Inactive. This might happen if log hits happen just about the
  2009. // TODO: same time (race condition in UlpCheckRawFile) with our closing
  2010. // TODO: the existing file ( but not recycling though ) .
  2011. if (pEntry->pLogFile==NULL || !NT_SUCCESS(Status))
  2012. {
  2013. //
  2014. // If somehow the logging ceased and handle released,
  2015. // it happens when the recycle isn't able to write to
  2016. // the log drive.
  2017. //
  2018. return Status;
  2019. }
  2020. pLogBuffer = pEntry->LogBuffer;
  2021. if (pLogBuffer)
  2022. {
  2023. //
  2024. // There are two conditions we execute the following if block
  2025. // 1. We were blocked on eresource exclusive and before us some
  2026. // other thread already take care of the buffer flush or recycling.
  2027. // 2. Reconfiguration happened and log attempt needs to write the
  2028. // title again.
  2029. //
  2030. if (BytesRequiredPlusHeader + pLogBuffer->BufferUsed <= g_UlLogBufferSize)
  2031. {
  2032. //
  2033. // If this is the first log attempt after a reconfig, then we have
  2034. // to write the title here. Reconfig doesn't immediately write the
  2035. // title but rather depend on us by setting the HeaderWritten flag
  2036. // to false.
  2037. //
  2038. if (!pEntry->Flags.HeaderWritten)
  2039. {
  2040. ULONG BytesCopied =
  2041. UlpRawCopyLogHeader(
  2042. pLogBuffer->Buffer + pLogBuffer->BufferUsed
  2043. );
  2044. pLogBuffer->BufferUsed += BytesCopied;
  2045. ASSERT(BytesCopied == sizeof(HTTP_RAW_FILE_HEADER));
  2046. pEntry->Flags.HeaderWritten = 1;
  2047. pEntry->Flags.HeaderFlushPending = 1;
  2048. }
  2049. pBufferWritter(
  2050. pContext,
  2051. pLogBuffer->Buffer + pLogBuffer->BufferUsed,
  2052. BytesRequired
  2053. );
  2054. pLogBuffer->BufferUsed += BytesRequired;
  2055. if (pBinaryIndexWritten &&
  2056. 0 == pEntry->Flags.CacheFlushInProgress)
  2057. {
  2058. InterlockedExchange(pBinaryIndexWritten, 1);
  2059. }
  2060. return STATUS_SUCCESS;
  2061. }
  2062. //
  2063. // Flush out the buffer first then proceed with allocating a new one.
  2064. //
  2065. Status = UlpFlushRawLogFile(pEntry);
  2066. if (!NT_SUCCESS(Status))
  2067. {
  2068. return Status;
  2069. }
  2070. }
  2071. ASSERT(pEntry->LogBuffer == NULL);
  2072. //
  2073. // This can be the very first log attempt or the previous allocation
  2074. // of LogBuffer failed, or the previous hit flushed and deallocated
  2075. // the old buffer. In either case, we allocate a new one,append the
  2076. // (title plus) new record and return for more/shared processing.
  2077. //
  2078. pLogBuffer = pEntry->LogBuffer = UlPplAllocateLogFileBuffer();
  2079. if (pLogBuffer == NULL)
  2080. {
  2081. return STATUS_NO_MEMORY;
  2082. }
  2083. //
  2084. // Very first attempt needs to write the title, as well as the attempt
  2085. // which causes the log file recycling. Both cases comes down here
  2086. //
  2087. if (!pEntry->Flags.HeaderWritten)
  2088. {
  2089. ULONG BytesCopied =
  2090. UlpRawCopyLogHeader(
  2091. pLogBuffer->Buffer + pLogBuffer->BufferUsed
  2092. );
  2093. pLogBuffer->BufferUsed += BytesCopied;
  2094. ASSERT(BytesCopied == sizeof(HTTP_RAW_FILE_HEADER));
  2095. pEntry->Flags.HeaderWritten = 1;
  2096. pEntry->Flags.HeaderFlushPending = 1;
  2097. }
  2098. pBufferWritter(
  2099. pContext,
  2100. pLogBuffer->Buffer + pLogBuffer->BufferUsed,
  2101. BytesRequired
  2102. );
  2103. pLogBuffer->BufferUsed += BytesRequired;
  2104. if (pBinaryIndexWritten &&
  2105. 0 == pEntry->Flags.CacheFlushInProgress)
  2106. {
  2107. InterlockedExchange(pBinaryIndexWritten, 1);
  2108. }
  2109. return STATUS_SUCCESS;
  2110. }
  2111. /***************************************************************************++
  2112. Routine Description:
  2113. Tries shared write first, if fails then it goes for exclusice lock and
  2114. flushes and/or recycles the file.
  2115. Arguments:
  2116. pEntry - The binary log file entry we are working on.
  2117. pUriEntry - Uri cache entry if this was for a cache hit, or NULL.
  2118. BytesRequired - The amount (in bytes) of data will be written.
  2119. pBufferWritter - Caller provided writer function.
  2120. pContext - Necessary context for the writer function.
  2121. --***************************************************************************/
  2122. NTSTATUS
  2123. UlpWriteToRawLogFile(
  2124. IN PUL_BINARY_LOG_FILE_ENTRY pEntry,
  2125. IN PUL_URI_CACHE_ENTRY pUriEntry,
  2126. IN ULONG RecordSize,
  2127. IN PUL_RAW_LOG_COPIER pBufferWritter,
  2128. IN PVOID pContext
  2129. )
  2130. {
  2131. NTSTATUS Status;
  2132. PLONG pIndexWritten; // Pointer to the cache entries index state
  2133. ULONG BytesRequired; // Total record size (including the index)
  2134. //
  2135. // Small macro which will increment the total record size, only if the
  2136. // index is not written yet. This must be used inside the entry pushlock.
  2137. // This is only for the cache hits.
  2138. //
  2139. #define UPDATE_FOR_INDEX_RECORD() \
  2140. if (NULL != pUriEntry && \
  2141. 0 == pUriEntry->BinaryIndexWritten) \
  2142. { \
  2143. BytesRequired = \
  2144. (RecordSize + UlpGetIndexRecordSize(pUriEntry)); \
  2145. \
  2146. pIndexWritten = \
  2147. (PLONG) &pUriEntry->BinaryIndexWritten; \
  2148. } \
  2149. else \
  2150. { \
  2151. BytesRequired = RecordSize; \
  2152. pIndexWritten = NULL; \
  2153. }
  2154. //
  2155. // Sanity check
  2156. //
  2157. PAGED_CODE();
  2158. ASSERT(IS_VALID_BINARY_LOG_FILE_ENTRY(pEntry));
  2159. ASSERT(RecordSize);
  2160. ASSERT(pBufferWritter);
  2161. ASSERT(pContext);
  2162. UlTrace(BINARY_LOGGING,
  2163. ("Http!UlpWriteToRawLogFile: pEntry %p\n", pEntry));
  2164. if (g_UlDisableLogBuffering)
  2165. {
  2166. //
  2167. // Above global variable is safe to look, it doesn't get changed
  2168. // during the life-time of the driver. It's get initialized from
  2169. // the registry and disables the log buffering.
  2170. //
  2171. UlAcquirePushLockExclusive(&pEntry->PushLock);
  2172. UPDATE_FOR_INDEX_RECORD();
  2173. Status = UlpWriteToRawLogFileDebug(
  2174. pEntry,
  2175. BytesRequired,
  2176. pBufferWritter,
  2177. pContext,
  2178. pIndexWritten
  2179. );
  2180. UlReleasePushLockExclusive(&pEntry->PushLock);
  2181. return Status;
  2182. }
  2183. //
  2184. // Try Shared write first which merely moves the BufferUsed forward
  2185. // and copy the pContext to the file buffer.
  2186. //
  2187. UlAcquirePushLockShared(&pEntry->PushLock);
  2188. UPDATE_FOR_INDEX_RECORD();
  2189. Status = UlpWriteToRawLogFileShared(
  2190. pEntry,
  2191. BytesRequired,
  2192. pBufferWritter,
  2193. pContext,
  2194. pIndexWritten
  2195. );
  2196. UlReleasePushLockShared(&pEntry->PushLock);
  2197. if (Status == STATUS_MORE_PROCESSING_REQUIRED)
  2198. {
  2199. //
  2200. // If shared write returns STATUS_MORE_PROCESSING_REQUIRED,
  2201. // we need to flush/recycle the buffer and try to log again.
  2202. // This time, we need to take the entry eresource exclusive.
  2203. //
  2204. UlAcquirePushLockExclusive(&pEntry->PushLock);
  2205. UPDATE_FOR_INDEX_RECORD();
  2206. Status = UlpWriteToRawLogFileExclusive(
  2207. pEntry,
  2208. BytesRequired,
  2209. pBufferWritter,
  2210. pContext,
  2211. pIndexWritten
  2212. );
  2213. UlReleasePushLockExclusive(&pEntry->PushLock);
  2214. }
  2215. return Status;
  2216. }
  2217. /***************************************************************************++
  2218. Routine Description:
  2219. Removes a binary log file entry, closes corresponding log file. Cleans
  2220. up control channel's directory string.
  2221. Arguments:
  2222. pControlChannel - Control channel whose log file to be removed.
  2223. --***************************************************************************/
  2224. VOID
  2225. UlRemoveBinaryLogEntry(
  2226. IN OUT PUL_CONTROL_CHANNEL pControlChannel
  2227. )
  2228. {
  2229. PUL_BINARY_LOG_FILE_ENTRY pEntry;
  2230. //
  2231. // We can safely clean up here. Because there are no longer requests
  2232. // holding an indirect pointer back to control channel. The refcount
  2233. // on control channel reached zero. No cgroups & no requests. Way to
  2234. // go.
  2235. //
  2236. PAGED_CODE();
  2237. //
  2238. // Clean up config group's directory string.
  2239. //
  2240. InterlockedExchange((PLONG)&g_BinaryLogEntryCount, 0);
  2241. if (pControlChannel->BinaryLoggingConfig.LogFileDir.Buffer)
  2242. {
  2243. UL_FREE_POOL(
  2244. pControlChannel->BinaryLoggingConfig.LogFileDir.Buffer,
  2245. UL_CG_LOGDIR_POOL_TAG );
  2246. pControlChannel->BinaryLoggingConfig.LogFileDir.Buffer = NULL;
  2247. pControlChannel->BinaryLoggingConfig.LogFileDir.Length = 0;
  2248. }
  2249. pEntry = pControlChannel->pBinaryLogEntry;
  2250. if (pEntry == NULL)
  2251. {
  2252. return;
  2253. }
  2254. UlAcquirePushLockExclusive(&pEntry->PushLock);
  2255. ASSERT(IS_VALID_BINARY_LOG_FILE_ENTRY(pEntry));
  2256. if (pEntry->pLogFile != NULL)
  2257. {
  2258. //
  2259. // Flush the buffer, close the file and mark the entry
  2260. // inactive.
  2261. //
  2262. UlpDisableBinaryEntry(pEntry);
  2263. }
  2264. //
  2265. // Free up the FileName (allocated when the entry becomes active
  2266. // otherwise it's empty)
  2267. //
  2268. if (pEntry->FileName.Buffer)
  2269. {
  2270. UL_FREE_POOL(pEntry->FileName.Buffer,UL_CG_LOGDIR_POOL_TAG);
  2271. pEntry->FileName.Buffer = NULL;
  2272. }
  2273. if (pEntry->LogBuffer)
  2274. {
  2275. UlPplFreeLogFileBuffer(pEntry->LogBuffer);
  2276. }
  2277. UlReleasePushLockExclusive(&pEntry->PushLock);
  2278. UlTrace(BINARY_LOGGING,
  2279. ("Http!UlRemoveBinaryLogEntry: pEntry %p closed.\n",
  2280. pEntry
  2281. ));
  2282. }
  2283. /***************************************************************************++
  2284. Routine Description:
  2285. This function implements the logging reconfiguration per attribute.
  2286. Everytime config changes happens we try to update the existing logging
  2287. parameters here.
  2288. Arguments:
  2289. pControlChannel - control channel that holds the binary log.
  2290. pCfgCurrent - Current binary logging config on the control channel
  2291. pCfgNew - New binary logging config passed down by the user.
  2292. --***************************************************************************/
  2293. NTSTATUS
  2294. UlReConfigureBinaryLogEntry(
  2295. IN OUT PUL_CONTROL_CHANNEL pControlChannel,
  2296. IN PHTTP_CONTROL_CHANNEL_BINARY_LOGGING pCfgCurrent,
  2297. IN PHTTP_CONTROL_CHANNEL_BINARY_LOGGING pCfgNew
  2298. )
  2299. {
  2300. NTSTATUS Status = STATUS_SUCCESS;
  2301. PUL_BINARY_LOG_FILE_ENTRY pEntry;
  2302. BOOLEAN HaveToReCycle = FALSE;
  2303. //
  2304. // Sanity check first
  2305. //
  2306. PAGED_CODE();
  2307. UlTrace(BINARY_LOGGING,("Http!UlReConfigureBinaryLogEntry: entry %p\n",
  2308. pControlChannel->pBinaryLogEntry));
  2309. //
  2310. // Discard the configuration changes when logging stays disabled.
  2311. //
  2312. if (pCfgCurrent->LoggingEnabled==FALSE && pCfgNew->LoggingEnabled==FALSE)
  2313. {
  2314. return Status;
  2315. }
  2316. //
  2317. // Note that we do not touch any of the params in the new config if it's
  2318. // state is disabled. We don't actualy check them in this case.
  2319. //
  2320. pEntry = pControlChannel->pBinaryLogEntry;
  2321. ASSERT(IS_VALID_BINARY_LOG_FILE_ENTRY(pEntry));
  2322. UlAcquirePushLockExclusive(&pEntry->PushLock);
  2323. if (pCfgCurrent->LoggingEnabled==TRUE && pCfgNew->LoggingEnabled==FALSE)
  2324. {
  2325. //
  2326. // Disable the entry if necessary.
  2327. //
  2328. if (pEntry->Flags.Active == 1)
  2329. {
  2330. //
  2331. // Once the entry is disabled, it will be enabled when next
  2332. // hit happens. And that obviously cannot happen before the
  2333. // control channel enables the binary logging back.
  2334. //
  2335. Status = UlpDisableBinaryEntry(pEntry);
  2336. }
  2337. pCfgCurrent->LoggingEnabled = FALSE;
  2338. goto end;
  2339. }
  2340. else
  2341. {
  2342. pCfgCurrent->LoggingEnabled = TRUE;
  2343. }
  2344. //
  2345. // If LogEntry is Inactive (means no request served for this site yet and
  2346. // the LogFile itself hasn't been created yet), all we have to do is flush
  2347. // the settings on the LogEntry, the cgroup and then return.
  2348. //
  2349. if (!pEntry->Flags.Active)
  2350. {
  2351. ASSERT(pEntry->pLogFile == NULL);
  2352. if (RtlCompareUnicodeString(&pCfgNew->LogFileDir,
  2353. &pCfgCurrent->LogFileDir, TRUE)
  2354. != 0)
  2355. {
  2356. //
  2357. // Store the new directory in the cgroup even if the entry is
  2358. // inactive. Discard the return value, if failure happens we
  2359. // keep the old directory.
  2360. //
  2361. UlCopyLogFileDir(
  2362. &pCfgCurrent->LogFileDir,
  2363. &pCfgNew->LogFileDir
  2364. );
  2365. //
  2366. // If creation fails later, we should event log.
  2367. //
  2368. pEntry->Flags.CreateFileFailureLogged = 0;
  2369. }
  2370. pEntry->Period = (HTTP_LOGGING_PERIOD) pCfgNew->LogPeriod;
  2371. pCfgCurrent->LogPeriod = pCfgNew->LogPeriod;
  2372. pEntry->TruncateSize = pCfgNew->LogFileTruncateSize;
  2373. pCfgCurrent->LogFileTruncateSize = pCfgNew->LogFileTruncateSize;
  2374. pCfgCurrent->LocaltimeRollover = pCfgNew->LocaltimeRollover;
  2375. pEntry->Flags.LocaltimeRollover = (pCfgNew->LocaltimeRollover ? 1 : 0);
  2376. goto end;
  2377. }
  2378. //
  2379. // If the entry was active then proceed down to do the reconfiguration
  2380. // and recyle immediately if it's necessary.
  2381. //
  2382. Status = UlCheckLogDirectory(&pCfgNew->LogFileDir);
  2383. if (!NT_SUCCESS(Status))
  2384. {
  2385. // Otherwise keep the old settings
  2386. goto end;
  2387. }
  2388. if (RtlCompareUnicodeString(
  2389. &pCfgNew->LogFileDir, &pCfgCurrent->LogFileDir, TRUE) != 0)
  2390. {
  2391. //
  2392. // Store the new directory in the config group.
  2393. //
  2394. Status = UlCopyLogFileDir(&pCfgCurrent->LogFileDir,
  2395. &pCfgNew->LogFileDir
  2396. );
  2397. if (!NT_SUCCESS(Status))
  2398. {
  2399. goto end;
  2400. }
  2401. //
  2402. // Rebuild the fully qualified file name.
  2403. //
  2404. Status = UlRefreshFileName(&pCfgCurrent->LogFileDir,
  2405. &pEntry->FileName,
  2406. &pEntry->pShortName
  2407. );
  2408. if (!NT_SUCCESS(Status))
  2409. {
  2410. goto end;
  2411. }
  2412. //
  2413. // Set the sequence number stale so that the recylcler below can
  2414. // obtain the proper number by scanning the directory.
  2415. //
  2416. pEntry->Flags.StaleSequenceNumber = 1;
  2417. HaveToReCycle = TRUE;
  2418. }
  2419. if (pCfgNew->LogPeriod != pCfgCurrent->LogPeriod)
  2420. {
  2421. pCfgCurrent->LogPeriod = pCfgNew->LogPeriod;
  2422. pEntry->Period = (HTTP_LOGGING_PERIOD) pCfgNew->LogPeriod;
  2423. pEntry->Flags.StaleTimeToExpire = 1;
  2424. pEntry->Flags.StaleSequenceNumber = 1;
  2425. HaveToReCycle = TRUE;
  2426. }
  2427. if (pCfgNew->LogFileTruncateSize != pCfgCurrent->LogFileTruncateSize)
  2428. {
  2429. if (TRUE == UlUpdateLogTruncateSize(
  2430. pCfgNew->LogFileTruncateSize,
  2431. &pCfgCurrent->LogFileTruncateSize,
  2432. &pEntry->TruncateSize,
  2433. pEntry->TotalWritten
  2434. ))
  2435. {
  2436. HaveToReCycle = TRUE;
  2437. }
  2438. }
  2439. if (pCfgNew->LocaltimeRollover != pCfgCurrent->LocaltimeRollover)
  2440. {
  2441. //
  2442. // Need to reclycle if the format is W3C.
  2443. //
  2444. pCfgCurrent->LocaltimeRollover = pCfgNew->LocaltimeRollover;
  2445. pEntry->Flags.LocaltimeRollover = (pCfgNew->LocaltimeRollover ? 1 : 0);
  2446. // TODO: HaveToReCycle = TRUE;
  2447. }
  2448. if (HaveToReCycle)
  2449. {
  2450. //
  2451. // Mark the entry inactive and postpone the recycle until the next
  2452. // request arrives.
  2453. //
  2454. Status = UlpDisableBinaryEntry(pEntry);
  2455. }
  2456. end:
  2457. if (!NT_SUCCESS(Status))
  2458. {
  2459. UlTrace(BINARY_LOGGING,
  2460. ("Http!UlReConfigureBinaryLogEntry: entry %p, failure %08lx\n",
  2461. pEntry,
  2462. Status
  2463. ));
  2464. }
  2465. UlReleasePushLockExclusive(&pEntry->PushLock);
  2466. return Status;
  2467. } // UlReConfigureLogEntry
  2468. /***************************************************************************++
  2469. Routine Description:
  2470. Get called by cache whenever the Uri cache is flushed.
  2471. If we have a binary log file with cache index records.
  2472. We write a notification record to warn the parser to reset
  2473. its hash table.
  2474. pControlChannel : which owns the binary log entry.
  2475. --***************************************************************************/
  2476. VOID
  2477. UlHandleCacheFlushedNotification(
  2478. IN PUL_CONTROL_CHANNEL pControlChannel
  2479. )
  2480. {
  2481. NTSTATUS Status = STATUS_SUCCESS;
  2482. PUL_BINARY_LOG_FILE_ENTRY pEntry = NULL;
  2483. ULONG BytesRequired = sizeof(HTTP_RAW_FILE_CACHE_NOTIFICATION);
  2484. HTTP_RAW_FILE_CACHE_NOTIFICATION NotificationRecord;
  2485. //
  2486. // Sanity checks.
  2487. //
  2488. PAGED_CODE();
  2489. ASSERT(IS_VALID_CONTROL_CHANNEL(pControlChannel));
  2490. //
  2491. // Quickly return, if we don't need to do anything.
  2492. //
  2493. if (!UlBinaryLoggingEnabled(pControlChannel))
  2494. {
  2495. return;
  2496. }
  2497. pEntry = pControlChannel->pBinaryLogEntry;
  2498. ASSERT(IS_VALID_BINARY_LOG_FILE_ENTRY(pEntry));
  2499. UlAcquirePushLockExclusive(&pEntry->PushLock);
  2500. //
  2501. // If entry doesn't have any log file or inactive,
  2502. // it should be due to recycling and it's not necessary
  2503. // to write a notification record since the parser
  2504. // should reset its hash table with every new file
  2505. // anyway.
  2506. //
  2507. if (pEntry->Flags.Active && pEntry->pLogFile &&
  2508. !UlpIsRawLogFileOverFlow(pEntry,BytesRequired))
  2509. {
  2510. //
  2511. // Write the record only if we have cache records
  2512. // in this binary log file.
  2513. //
  2514. if (1 == InterlockedCompareExchange(
  2515. (PLONG) &pEntry->ServedCacheHit,
  2516. 0,
  2517. 1))
  2518. {
  2519. NotificationRecord.RecordType
  2520. = HTTP_RAW_RECORD_CACHE_NOTIFICATION_DATA_TYPE;
  2521. //
  2522. // Call to the exclusive writer,
  2523. //
  2524. Status = UlpWriteToRawLogFileExclusive(
  2525. pEntry,
  2526. BytesRequired,
  2527. UlpRawCopyCacheNotification,
  2528. &NotificationRecord,
  2529. NULL
  2530. );
  2531. UlTrace(BINARY_LOGGING,
  2532. ("Http!UlHandleCacheFlushedNotification: pEntry %p Status %08lx\n",
  2533. pEntry,
  2534. Status
  2535. ));
  2536. }
  2537. }
  2538. //
  2539. // Enable the indexing for the cache hits. Because we are done with
  2540. // writing the flush record.
  2541. //
  2542. pEntry->Flags.CacheFlushInProgress = 0;
  2543. UlReleasePushLockExclusive(&pEntry->PushLock);
  2544. return;
  2545. }
  2546. /***************************************************************************++
  2547. Routine Description:
  2548. Simple routine to copy the cache notification record to binary
  2549. log file buffer.
  2550. --***************************************************************************/
  2551. VOID
  2552. UlpRawCopyCacheNotification(
  2553. IN PVOID pContext,
  2554. IN PUCHAR pBuffer,
  2555. IN ULONG BytesRequired
  2556. )
  2557. {
  2558. PHTTP_RAW_FILE_CACHE_NOTIFICATION pNotification;
  2559. PUCHAR pCurrentBufferPtr;
  2560. UNREFERENCED_PARAMETER(BytesRequired);
  2561. PAGED_CODE();
  2562. ASSERT(pContext);
  2563. ASSERT(pBuffer);
  2564. UlTrace(BINARY_LOGGING,
  2565. ("Http!UlpRawCopyCacheNotification: pBuffer %p\n", pBuffer ));
  2566. ASSERT(pBuffer == ALIGN_UP_POINTER(pBuffer,PVOID));
  2567. pCurrentBufferPtr = pBuffer;
  2568. pNotification = (PHTTP_RAW_FILE_CACHE_NOTIFICATION) pBuffer;
  2569. pNotification->RecordType =
  2570. ((PHTTP_RAW_FILE_CACHE_NOTIFICATION) pContext)->RecordType;
  2571. ASSERT(pNotification->RecordType ==
  2572. HTTP_RAW_RECORD_CACHE_NOTIFICATION_DATA_TYPE);
  2573. pCurrentBufferPtr += sizeof(HTTP_RAW_FILE_CACHE_NOTIFICATION);
  2574. ASSERT(pCurrentBufferPtr ==
  2575. (PUCHAR) ALIGN_UP_POINTER(pCurrentBufferPtr, PVOID));
  2576. ASSERT(BytesRequired == DIFF(pCurrentBufferPtr-pBuffer));
  2577. return;
  2578. }
  2579. /***************************************************************************++
  2580. Routine Description:
  2581. Get called by cache * just before * the Uri cache is flushed.
  2582. At this time we set the CacheFlushInProgress flag to temporarly disable
  2583. the indexing for the cache hits. Every cache hit will generate index
  2584. records until this flag is cleared.
  2585. The flag will be cleared when the flush notification itself is written.
  2586. pControlChannel : which owns the binary log entry.
  2587. --***************************************************************************/
  2588. VOID
  2589. UlDisableIndexingForCacheHits(
  2590. IN PUL_CONTROL_CHANNEL pControlChannel
  2591. )
  2592. {
  2593. PUL_BINARY_LOG_FILE_ENTRY pEntry;
  2594. //
  2595. // Sanity checks.
  2596. //
  2597. PAGED_CODE();
  2598. ASSERT(IS_VALID_CONTROL_CHANNEL(pControlChannel));
  2599. //
  2600. // Quickly return, if we don't need to do anything.
  2601. //
  2602. if (!UlBinaryLoggingEnabled(pControlChannel))
  2603. {
  2604. return;
  2605. }
  2606. //
  2607. // Need to acquire the lock exclusive to block other cache hits
  2608. // until we set the flag.
  2609. //
  2610. pEntry = pControlChannel->pBinaryLogEntry;
  2611. ASSERT(IS_VALID_BINARY_LOG_FILE_ENTRY(pEntry));
  2612. UlAcquirePushLockExclusive(&pEntry->PushLock);
  2613. pEntry->Flags.CacheFlushInProgress = 1;
  2614. UlReleasePushLockExclusive(&pEntry->PushLock);
  2615. return;
  2616. }