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

7423 lines
203 KiB

  1. /*++
  2. Copyright (c) 2000-2001 Microsoft Corporation
  3. Module Name:
  4. ullog.cxx (UL IIS6 HIT Logging)
  5. Abstract:
  6. This module implements the logging facilities
  7. for IIS6 including the NCSA, IIS and W3CE types
  8. of logging.
  9. Author:
  10. Ali E. Turkoglu (aliTu) 10-May-2000
  11. Revision History:
  12. --*/
  13. #include "precomp.h"
  14. #include "iiscnfg.h"
  15. #include "ullogp.h"
  16. //
  17. // Generic Private globals.
  18. //
  19. LIST_ENTRY g_LogListHead = {NULL,NULL};
  20. LONG g_LogListEntryCount = 0;
  21. BOOLEAN g_InitLogsCalled = FALSE;
  22. CHAR g_GMTOffset[SIZE_OF_GMT_OFFSET+1];
  23. BOOLEAN g_UTF8Logging = FALSE;
  24. //
  25. // For Log Buffering
  26. //
  27. ULONG g_AllocationGranularity = 0;
  28. KTIMER g_BufferTimer;
  29. BOOLEAN g_BufferTimerInitialized = FALSE;
  30. UL_SPIN_LOCK g_BufferTimerSpinLock;
  31. KDPC g_BufferTimerDpcObject;
  32. //
  33. // For Logging Date & Time caching
  34. //
  35. UL_LOG_DATE_AND_TIME_CACHE
  36. g_UlDateTimeCache[HttpLoggingTypeMaximum];
  37. LARGE_INTEGER g_UlLogSystemTime;
  38. FAST_MUTEX g_LogCacheFastMutex;
  39. //
  40. // For Log Cycling
  41. //
  42. KDPC g_LogTimerDpcObject;
  43. KTIMER g_LogTimer;
  44. BOOLEAN g_LogTimerInitialized = FALSE;
  45. BOOLEAN g_LogTimerStarted = FALSE;
  46. BOOLEAN g_BufferTimerStarted = FALSE;
  47. // This spinlock covers both g_BufferTimerStarted and
  48. // g_LogTimerStarted variables
  49. UL_SPIN_LOCK g_LogTimerSpinLock;
  50. //
  51. // Used to wait for endpoints to close on shutdown
  52. //
  53. UL_SPIN_LOCK g_BufferIoSpinLock;
  54. BOOLEAN g_BufferWaitingForIoComplete = FALSE;
  55. KEVENT g_BufferIoCompleteEvent;
  56. ULONG g_BufferIoCount = 0;
  57. #ifdef ALLOC_PRAGMA
  58. #pragma alloc_text( INIT, UlInitializeLogs )
  59. #pragma alloc_text( PAGE, UlTerminateLogs )
  60. #pragma alloc_text( PAGE, UlpGetGMTOffset )
  61. #pragma alloc_text( PAGE, UlpRecycleLogFile )
  62. #pragma alloc_text( PAGE, UlpGetLogFileLength )
  63. #pragma alloc_text( PAGE, UlCreateLog )
  64. #pragma alloc_text( PAGE, UlRemoveLogFileEntry )
  65. #pragma alloc_text( PAGE, UlpConstructLogFileEntry )
  66. #pragma alloc_text( PAGE, UlProbeLogData )
  67. #pragma alloc_text( PAGE, UlAllocateLogDataBuffer )
  68. #pragma alloc_text( PAGE, UlpConstructFileName )
  69. #pragma alloc_text( PAGE, UlpUpdateLogFlags )
  70. #pragma alloc_text( PAGE, UlpUpdateLogTruncateSize )
  71. #pragma alloc_text( PAGE, UlpUpdatePeriod )
  72. #pragma alloc_text( PAGE, UlpUpdateFormat )
  73. #pragma alloc_text( PAGE, UlReConfigureLogEntry )
  74. #pragma alloc_text( PAGE, UlpGrowLogEntry )
  75. #pragma alloc_text( PAGE, UlLogTimerHandler )
  76. #pragma alloc_text( PAGE, UlBufferTimerHandler )
  77. #pragma alloc_text( PAGE, UlpCalculateTimeToExpire )
  78. #pragma alloc_text( PAGE, UlpCreateSafeDirectory )
  79. #pragma alloc_text( PAGE, UlpAppendW3CLogTitle )
  80. #pragma alloc_text( PAGE, UlpWriteToLogFile )
  81. #pragma alloc_text( PAGE, UlpFlushLogFile )
  82. #pragma alloc_text( PAGE, UlCheckLogDirectory )
  83. #pragma alloc_text( PAGE, UlSetUTF8Logging )
  84. #pragma alloc_text( PAGE, UlCaptureLogFieldsW3C )
  85. #pragma alloc_text( PAGE, UlCaptureLogFieldsNCSA )
  86. #pragma alloc_text( PAGE, UlCaptureLogFieldsIIS )
  87. #pragma alloc_text( PAGE, UlLogHttpCacheHit )
  88. #pragma alloc_text( PAGE, UlLogHttpCacheHitWorker )
  89. #pragma alloc_text( PAGE, UlLogHttpHit )
  90. #pragma alloc_text( PAGE, UlpGenerateDateAndTimeFields )
  91. #pragma alloc_text( PAGE, UlpGetDateTimeFields )
  92. #pragma alloc_text( PAGE, UlpLogCloseHandle )
  93. #pragma alloc_text( PAGE, UlpLogCloseHandleWorker )
  94. #endif // ALLOC_PRAGMA
  95. #if 0
  96. NOT PAGEABLE -- UlLogTimerDpcRoutine
  97. NOT PAGEABLE -- UlpTerminateLogTimer
  98. NOT PAGEABLE -- UlpInsertLogFileEntry
  99. NOT PAGEABLE -- UlpSetLogTimer
  100. NOT PAGEABLE -- UlpSetBufferTimer
  101. NOT PAGEABLE -- UlBufferTimerDpcRoutine
  102. NOT PAGEABLE -- UlpTerminateTimers
  103. NOT PAGEABLE -- UlpInitializeTimers
  104. NOT PAGEABLE -- UlWaitForBufferIoToComplete
  105. NOT PAGEABLE -- UlpBufferFlushAPC
  106. NOT PAGEABLE -- UlDestroyLogDataBuffer
  107. NOT PAGEABLE -- UlDestroyLogDataBufferWorker
  108. #endif
  109. //
  110. // Public functions.
  111. //
  112. /***************************************************************************++
  113. Routine Description:
  114. UlInitializeLogs :
  115. Initialize the resource for log list synchronization
  116. --***************************************************************************/
  117. NTSTATUS
  118. UlInitializeLogs (
  119. VOID
  120. )
  121. {
  122. NTSTATUS Status = STATUS_SUCCESS;
  123. PAGED_CODE();
  124. ASSERT(!g_InitLogsCalled);
  125. if (!g_InitLogsCalled)
  126. {
  127. InitializeListHead(&g_LogListHead);
  128. Status = UlInitializeResource(
  129. &g_pUlNonpagedData->LogListResource,
  130. "LogListResource",
  131. 0,
  132. UL_LOG_LIST_RESOURCE_TAG
  133. );
  134. ASSERT(NT_SUCCESS(Status)); // the call always returns success
  135. if (!NT_SUCCESS(Status))
  136. {
  137. return Status;
  138. }
  139. g_InitLogsCalled = TRUE;
  140. UlpInitializeTimers();
  141. UlpInitializeLogCache();
  142. UlpGetGMTOffset();
  143. UlInitializeSpinLock( &g_BufferIoSpinLock, "g_BufferIoSpinLock" );
  144. //
  145. // Get the allocation granularity from the system
  146. // It will be used as log buffer size if there's no registry
  147. // overwrites.
  148. //
  149. UlpInitializeLogBufferGranularity();
  150. if (g_AllocationGranularity == 0)
  151. {
  152. g_AllocationGranularity = DEFAULT_MAX_LOG_BUFFER_SIZE;
  153. }
  154. //
  155. // Overwrite the log buffer size with the above value
  156. // if registry parameter doesn't exist.
  157. //
  158. if (g_UlLogBufferSize == 0)
  159. {
  160. // No registry parameter available use the system granularity
  161. g_UlLogBufferSize = g_AllocationGranularity;
  162. }
  163. else
  164. {
  165. // Proceed with using the registry provided log buffer size
  166. UlTrace( LOGGING,
  167. ("Ul!UlInitializeLogs: Log buffer size %d from registry!\n",
  168. g_UlLogBufferSize
  169. ));
  170. }
  171. }
  172. return Status;
  173. }
  174. /***************************************************************************++
  175. Routine Description:
  176. UlTerminateLogs :
  177. Deletes the resource for log list synchronization
  178. --***************************************************************************/
  179. VOID
  180. UlTerminateLogs(
  181. VOID
  182. )
  183. {
  184. NTSTATUS Status;
  185. PAGED_CODE();
  186. if (g_InitLogsCalled)
  187. {
  188. ASSERT( IsListEmpty( &g_LogListHead )) ;
  189. //
  190. // Make sure terminate the log timer before
  191. // deleting the log list resource
  192. //
  193. UlpTerminateTimers();
  194. Status = UlDeleteResource(
  195. &g_pUlNonpagedData->LogListResource
  196. );
  197. ASSERT(NT_SUCCESS(Status));
  198. UlWaitForBufferIoToComplete();
  199. g_InitLogsCalled = FALSE;
  200. }
  201. }
  202. /***************************************************************************++
  203. Routine Description:
  204. UlSetUTF8Logging :
  205. Sets the UTF8Logging on or off. Only once. Initially Utf8Logging is
  206. FALSE and it may only be set during the init once. Following possible
  207. changes won't be taken.
  208. ReConfiguration code is explicitly missing as WAS will anly call this
  209. only once (init) during the lifetime of the control channel.
  210. --***************************************************************************/
  211. NTSTATUS
  212. UlSetUTF8Logging (
  213. IN BOOLEAN UTF8Logging
  214. )
  215. {
  216. PLIST_ENTRY pLink;
  217. PUL_LOG_FILE_ENTRY pEntry;
  218. NTSTATUS Status;
  219. PAGED_CODE();
  220. Status = STATUS_SUCCESS;
  221. //
  222. // Update & Reycle. Need to acquire the logging resource to prevent
  223. // further log hits to be written to file before we finish our
  224. // business. recycle is necessary because files will be renamed to
  225. // have prefix "u_" once we enabled the UTF8.
  226. //
  227. UlTrace(LOGGING,("Ul!UlSetUTF8Logging: UTF8Logging Old %d -> New %d\n",
  228. g_UTF8Logging,UTF8Logging
  229. ));
  230. UlAcquireResourceExclusive(&g_pUlNonpagedData->LogListResource, TRUE);
  231. //
  232. // Drop the change if the setting is not changing.
  233. //
  234. if ( g_UTF8Logging == UTF8Logging )
  235. {
  236. goto end;
  237. }
  238. g_UTF8Logging = UTF8Logging;
  239. for (pLink = g_LogListHead.Flink;
  240. pLink != &g_LogListHead;
  241. pLink = pLink->Flink
  242. )
  243. {
  244. pEntry = CONTAINING_RECORD(
  245. pLink,
  246. UL_LOG_FILE_ENTRY,
  247. LogFileListEntry
  248. );
  249. // TODO: Investigate whether we really need to acquire this or not.
  250. UlAcquireResourceExclusive(&pEntry->EntryResource, TRUE);
  251. SET_SEQUNCE_NUMBER_STALE(pEntry);
  252. Status = UlpRecycleLogFile(pEntry);
  253. ASSERT(NT_SUCCESS(Status));
  254. UlReleaseResource(&pEntry->EntryResource);
  255. }
  256. end:
  257. UlReleaseResource(&g_pUlNonpagedData->LogListResource);
  258. return Status;
  259. }
  260. /***************************************************************************++
  261. Routine Description:
  262. UlpGetLogFileLength :
  263. A utility to get the log file length, for a possible size check
  264. Arguments:
  265. hFile - handle to file
  266. Return Value:
  267. ULONG - the length of the file
  268. --***************************************************************************/
  269. ULONG
  270. UlpGetLogFileLength(
  271. IN HANDLE hFile
  272. )
  273. {
  274. NTSTATUS Status;
  275. FILE_STANDARD_INFORMATION StandardInformation;
  276. IO_STATUS_BLOCK IoStatusBlock;
  277. ULONG Length;
  278. PAGED_CODE();
  279. Status = ZwQueryInformationFile(
  280. hFile,
  281. &IoStatusBlock,
  282. &StandardInformation,
  283. sizeof(StandardInformation),
  284. FileStandardInformation
  285. );
  286. ASSERT(NT_SUCCESS(Status));
  287. if (NT_SUCCESS(Status))
  288. {
  289. ASSERT(StandardInformation.EndOfFile.HighPart == 0);
  290. Length = StandardInformation.EndOfFile.LowPart;
  291. }
  292. else
  293. {
  294. Length = 0;
  295. }
  296. return Length;
  297. }
  298. /***************************************************************************++
  299. Routine Description:
  300. Will allocate/fill up a new UNICODE_STRING to hold the directory name info
  301. based on the LocalDrive/UNC.
  302. It's caller's responsibility to cleanup the unicode buffer. If return code
  303. is SUCCESS otherwise no buffer get allocated at all.
  304. Arguments:
  305. PWSTR - the directory name as it's received from the user.
  306. --***************************************************************************/
  307. NTSTATUS
  308. UlpBuildLogDirectory(
  309. IN PUNICODE_STRING pSrcDirName,
  310. IN OUT PUNICODE_STRING pDstDirName
  311. )
  312. {
  313. UNICODE_STRING PathPrefix;
  314. //
  315. // Sanity check.
  316. //
  317. PAGED_CODE();
  318. ASSERT(pSrcDirName);
  319. ASSERT(pDstDirName);
  320. UlTrace(LOGGING,(
  321. "Ul!UlpBuildLogDirectory: Directory %S,Length %d,MaxLength %d\n",
  322. pSrcDirName->Buffer,
  323. pSrcDirName->Length,
  324. pSrcDirName->MaximumLength
  325. ));
  326. // Allocate a buffer including the terminating NULL and the prefix.
  327. pDstDirName->Length = 0;
  328. pDstDirName->MaximumLength =
  329. pSrcDirName->Length + (UL_MAX_PATH_PREFIX_LENGTH+1) * sizeof(WCHAR);
  330. pDstDirName->Buffer =
  331. (PWSTR) UL_ALLOCATE_ARRAY(
  332. PagedPool,
  333. UCHAR,
  334. pDstDirName->MaximumLength,
  335. UL_CG_LOGDIR_POOL_TAG
  336. );
  337. if (pDstDirName->Buffer == NULL)
  338. {
  339. return STATUS_NO_MEMORY;
  340. }
  341. ASSERT(pSrcDirName->Length > sizeof(WCHAR));
  342. // We store the dir name to cgroup as it is. But when we are constructing
  343. // the filename we skip the second backslash for the UNC shares and for
  344. // local dirs w/o the drive names.
  345. if (pSrcDirName->Buffer[0] == L'\\')
  346. {
  347. if (pSrcDirName->Buffer[1] == L'\\')
  348. {
  349. // UNC share: "\\alitudev\temp"
  350. RtlInitUnicodeString( &PathPrefix, UL_UNC_PATH_PREFIX );
  351. }
  352. else
  353. {
  354. // Local Directory name is missing the device i.e "\temp"
  355. // It should be fully qualified name.
  356. UL_FREE_POOL( pDstDirName->Buffer, UL_CG_LOGDIR_POOL_TAG );
  357. pDstDirName->Buffer = NULL;
  358. return STATUS_NOT_SUPPORTED;
  359. }
  360. RtlCopyUnicodeString( pDstDirName, &PathPrefix );
  361. RtlCopyMemory(
  362. &pDstDirName->Buffer[pDstDirName->Length/sizeof(WCHAR)],
  363. &pSrcDirName->Buffer[1],
  364. pSrcDirName->Length - sizeof(WCHAR)
  365. );
  366. pDstDirName->Length += (pSrcDirName->Length - sizeof(WCHAR));
  367. pDstDirName->Buffer[pDstDirName->Length/sizeof(WCHAR)] = UNICODE_NULL;
  368. }
  369. else
  370. {
  371. RtlInitUnicodeString( &PathPrefix, UL_LOCAL_PATH_PREFIX );
  372. RtlCopyUnicodeString( pDstDirName, &PathPrefix );
  373. RtlAppendUnicodeStringToString( pDstDirName, pSrcDirName );
  374. }
  375. return STATUS_SUCCESS;
  376. }
  377. /***************************************************************************++
  378. Routine Description:
  379. A utility to check to see if the directory name is correct or not.
  380. Arguments:
  381. PWSTR - the directory name as it's received from the user.
  382. --***************************************************************************/
  383. NTSTATUS
  384. UlCheckLogDirectory(
  385. IN PUNICODE_STRING pDirName
  386. )
  387. {
  388. NTSTATUS Status;
  389. UNICODE_STRING PathPrefix;
  390. UNICODE_STRING DirectoryName;
  391. PWCHAR pwsz;
  392. //
  393. // Sanity check.
  394. //
  395. PAGED_CODE();
  396. Status = UlpBuildLogDirectory( pDirName, &DirectoryName );
  397. if (!NT_SUCCESS(Status))
  398. {
  399. goto end;
  400. }
  401. //
  402. // Create/Open the director(ies) to see whether it's correct or not.
  403. //
  404. Status = UlpCreateSafeDirectory( &DirectoryName );
  405. end:
  406. UlTrace(LOGGING,("Ul!UlCheckLogDirectory: [%S] -> [%S], Status %08lx\n",
  407. pDirName->Buffer,
  408. DirectoryName.Buffer,
  409. Status
  410. ));
  411. if (DirectoryName.Buffer)
  412. {
  413. UL_FREE_POOL( DirectoryName.Buffer, UL_CG_LOGDIR_POOL_TAG );
  414. }
  415. return Status;
  416. }
  417. /***************************************************************************++
  418. Routine Description:
  419. Everytime Aynsc Write Io happens on Log Buffer This APC get called when
  420. completion happens and decrement the global Io Count. If shutting down
  421. we set the event.
  422. This is basically to prevent against shutting down before the Io Complete.
  423. Arguments:
  424. None.
  425. --***************************************************************************/
  426. VOID
  427. UlpBufferFlushAPC(
  428. IN PVOID ApcContext,
  429. IN PIO_STATUS_BLOCK pIoStatusBlock,
  430. IN ULONG Reserved
  431. )
  432. {
  433. PUL_LOG_FILE_BUFFER pLogBuffer;
  434. ULONG IoCount;
  435. KIRQL OldIrql;
  436. UlTrace( LOGGING,("Ul!UlpBufferFlushAPC: entry %p and status %08lx Count %d\n",
  437. ApcContext,
  438. pIoStatusBlock->Status,
  439. (g_BufferIoCount - 1)
  440. ));
  441. //
  442. // Free the LogBuffer allocated for this write I/o.
  443. //
  444. pLogBuffer = (PUL_LOG_FILE_BUFFER) ApcContext;
  445. ASSERT(IS_VALID_LOG_FILE_BUFFER(pLogBuffer));
  446. UlPplFreeLogBuffer( pLogBuffer );
  447. //
  448. // Decrement the global outstanding i/o count.
  449. //
  450. IoCount = InterlockedDecrement((PLONG) &g_BufferIoCount);
  451. if ( IoCount == 0 )
  452. {
  453. UlAcquireSpinLock( &g_BufferIoSpinLock, &OldIrql );
  454. //
  455. // Set the event if we hit to zero and waiting for drain.
  456. //
  457. if ( g_BufferWaitingForIoComplete )
  458. {
  459. KeSetEvent( &g_BufferIoCompleteEvent, 0, FALSE );
  460. }
  461. UlReleaseSpinLock( &g_BufferIoSpinLock, OldIrql );
  462. }
  463. }
  464. /***************************************************************************++
  465. Routine Description:
  466. Waits for Io Completions to complete on Log Buffers before shutdown.
  467. Arguments:
  468. None.
  469. --***************************************************************************/
  470. VOID
  471. UlWaitForBufferIoToComplete(
  472. VOID
  473. )
  474. {
  475. KIRQL OldIrql;
  476. BOOLEAN Wait = FALSE;
  477. ASSERT( g_InitLogsCalled );
  478. if ( g_InitLogsCalled )
  479. {
  480. UlAcquireSpinLock( &g_BufferIoSpinLock, &OldIrql );
  481. if ( !g_BufferWaitingForIoComplete )
  482. {
  483. g_BufferWaitingForIoComplete = TRUE;
  484. KeInitializeEvent(
  485. &g_BufferIoCompleteEvent,
  486. NotificationEvent,
  487. FALSE
  488. );
  489. }
  490. //
  491. // If no more i/o operations are happening we are not going to
  492. // wait for them. It is not possible for global i/o counter to
  493. // increment at this time because the log file entry list is empty.
  494. // If there were outstanding i/o then we have to wait them to be
  495. // complete.
  496. //
  497. if ( g_BufferIoCount > 0 )
  498. {
  499. Wait = TRUE;
  500. }
  501. UlReleaseSpinLock( &g_BufferIoSpinLock, OldIrql );
  502. if (Wait)
  503. {
  504. KeWaitForSingleObject(
  505. (PVOID)&g_BufferIoCompleteEvent,
  506. UserRequest,
  507. KernelMode,
  508. FALSE,
  509. NULL
  510. );
  511. }
  512. }
  513. }
  514. /***************************************************************************++
  515. Routine Description:
  516. UlpFlushLogFile :
  517. Assumes you are holding the FastMutex for the buffer & loglist resource
  518. Arguments:
  519. pFile - Handle to a log file entry
  520. --***************************************************************************/
  521. NTSTATUS
  522. UlpFlushLogFile(
  523. IN PUL_LOG_FILE_ENTRY pFile
  524. )
  525. {
  526. NTSTATUS Status;
  527. LARGE_INTEGER EndOfFile;
  528. PUL_LOG_FILE_BUFFER pLogBuffer;
  529. PIO_STATUS_BLOCK pIoStatusBlock;
  530. PAGED_CODE();
  531. ASSERT(pFile!=NULL);
  532. UlTrace( LOGGING, ("Ul!UlpFlushLogFile: pEntry %p\n", pFile ));
  533. pLogBuffer = pFile->LogBuffer;
  534. if ( pLogBuffer==NULL || pLogBuffer->BufferUsed==0 || pFile->hFile==NULL )
  535. {
  536. return STATUS_SUCCESS;
  537. }
  538. pFile->LogBuffer = NULL;
  539. pIoStatusBlock = &pLogBuffer->IoStatusBlock;
  540. EndOfFile.HighPart = -1;
  541. EndOfFile.LowPart = FILE_WRITE_TO_END_OF_FILE;
  542. Status = ZwWriteFile(
  543. pFile->hFile,
  544. NULL,
  545. &UlpBufferFlushAPC,
  546. pLogBuffer,
  547. pIoStatusBlock,
  548. pLogBuffer->Buffer,
  549. pLogBuffer->BufferUsed,
  550. &EndOfFile,
  551. NULL
  552. );
  553. if ( !NT_SUCCESS(Status) )
  554. {
  555. //
  556. // Status maybe STATUS_DISK_FULL,in that case Logging
  557. // will be ceased. Hence log hits stored in this buffer
  558. // are lost.
  559. //
  560. UlTrace( LOGGING,
  561. ("Ul!UlpFlushLogFile: ZwWriteFile Failure %08lx \n",
  562. Status
  563. ));
  564. UlPplFreeLogBuffer( pLogBuffer );
  565. return Status;
  566. }
  567. //
  568. // Properly keep the number of outstanding Io
  569. //
  570. InterlockedIncrement( (PLONG) &g_BufferIoCount );
  571. return Status;
  572. }
  573. /***************************************************************************++
  574. Routine Description:
  575. UlpWriteToLogFile :
  576. Writes a record to a log file
  577. Arguments:
  578. pFile - Handle to a log file entry
  579. RecordSize - Length of the record to be written.
  580. pRecord - The log record to be written to the log buffer
  581. --***************************************************************************/
  582. NTSTATUS
  583. UlpWriteToLogFile(
  584. IN PUL_LOG_FILE_ENTRY pFile,
  585. IN ULONG RecordSize,
  586. IN PCHAR pRecord,
  587. IN ULONG UsedOffset1,
  588. IN ULONG UsedOffset2
  589. )
  590. {
  591. NTSTATUS Status;
  592. PAGED_CODE();
  593. ASSERT(pRecord!=NULL);
  594. ASSERT(RecordSize!=0);
  595. ASSERT(IS_VALID_LOG_FILE_ENTRY(pFile));
  596. UlTrace(LOGGING, ("Ul!UlpWriteToLogFile: pEntry %p\n", pFile));
  597. if ( pFile==NULL ||
  598. pRecord==NULL ||
  599. RecordSize==0 ||
  600. RecordSize>g_UlLogBufferSize
  601. )
  602. {
  603. return STATUS_INVALID_PARAMETER;
  604. }
  605. //
  606. // We are safe here by dealing only with entry eresource since the
  607. // time based recycling, reconfiguration and periodic buffer flushing
  608. // always acquires the global list eresource exclusively and we are
  609. // already holding it shared. But we should still be carefull about
  610. // file size based recyling and we should only do it while we are
  611. // holding the entries eresource exclusive.I.e. look at the exclusive
  612. // writer down below.
  613. //
  614. if (g_UlDebugLogBufferPeriod)
  615. {
  616. //
  617. // Above global variable is safe to look, it doesn't get changed
  618. // during the life-time of the driver. It's get initialized from
  619. // the registry and disables the log buffering.
  620. //
  621. UlAcquireResourceExclusive(&pFile->EntryResource, TRUE);
  622. Status = UlpWriteToLogFileDebug(
  623. pFile,
  624. RecordSize,
  625. pRecord,
  626. UsedOffset1,
  627. UsedOffset2
  628. );
  629. UlReleaseResource(&pFile->EntryResource);
  630. return Status;
  631. }
  632. //
  633. // Try UlpWriteToLogFileShared first which merely moves the
  634. // BufferUsed forward and copy the record to LogBuffer->Buffer.
  635. //
  636. UlAcquireResourceShared(&pFile->EntryResource, TRUE);
  637. Status = UlpWriteToLogFileShared(
  638. pFile,
  639. RecordSize,
  640. pRecord,
  641. UsedOffset1,
  642. UsedOffset2
  643. );
  644. UlReleaseResource(&pFile->EntryResource);
  645. if (Status == STATUS_MORE_PROCESSING_REQUIRED)
  646. {
  647. //
  648. // UlpWriteToLogFileShared returns STATUS_MORE_PROCESSING_REQUIRED,
  649. // we need to flush the buffer and try to log again. This time, we
  650. // need to take the entry eresource exclusive.
  651. //
  652. UlAcquireResourceExclusive(&pFile->EntryResource, TRUE);
  653. Status = UlpWriteToLogFileExclusive(
  654. pFile,
  655. RecordSize,
  656. pRecord,
  657. UsedOffset1,
  658. UsedOffset2
  659. );
  660. UlReleaseResource(&pFile->EntryResource);
  661. }
  662. return Status;
  663. }
  664. /***************************************************************************++
  665. Routine Description:
  666. UlpAppendToLogBuffer :
  667. Append a record to a log file
  668. REQUIRES you to hold the loglist resource shared and entry mutex
  669. shared or exclusive
  670. Arguments:
  671. pFile - Handle to a log file entry
  672. RecordSize - Length of the record to be written.
  673. pRecord - The log record to be written to the log buffer
  674. --***************************************************************************/
  675. __inline
  676. VOID
  677. FASTCALL
  678. UlpAppendToLogBuffer(
  679. IN PUL_LOG_FILE_ENTRY pFile,
  680. IN ULONG BufferUsed,
  681. IN ULONG RecordSize,
  682. IN PCHAR pRecord,
  683. IN ULONG UsedOffset1,
  684. IN ULONG UsedOffset2
  685. )
  686. {
  687. PUL_LOG_FILE_BUFFER pLogBuffer = pFile->LogBuffer;
  688. UlTrace( LOGGING, ("Ul!UlpAppendToLogBuffer: pEntry %p\n", pFile ));
  689. //
  690. // IIS format log line may be fragmented (identified by looking at the
  691. // UsedOffset2), handle it wisely.
  692. //
  693. if (UsedOffset2)
  694. {
  695. RtlCopyMemory(
  696. pLogBuffer->Buffer + BufferUsed,
  697. &pRecord[0],
  698. UsedOffset1
  699. );
  700. RtlCopyMemory(
  701. pLogBuffer->Buffer + BufferUsed + UsedOffset1,
  702. &pRecord[512],
  703. UsedOffset2
  704. );
  705. RtlCopyMemory(
  706. pLogBuffer->Buffer + BufferUsed + UsedOffset1 + UsedOffset2,
  707. &pRecord[1024],
  708. RecordSize - (UsedOffset1 + UsedOffset2)
  709. );
  710. }
  711. else
  712. {
  713. RtlCopyMemory(
  714. pLogBuffer->Buffer + BufferUsed,
  715. pRecord,
  716. RecordSize
  717. );
  718. }
  719. UlpIncrementBytesWritten(pFile, RecordSize);
  720. }
  721. /***************************************************************************++
  722. Routine Description:
  723. REQUIRES LogListResource Shared & Entry eresource exclusive.
  724. Appends the W3C log file title to the existing buffer.
  725. Arguments:
  726. pFile - Pointer to the logfile entry
  727. pCurrentTimeFields - Current time fields
  728. --***************************************************************************/
  729. NTSTATUS
  730. UlpAppendW3CLogTitle(
  731. IN PUL_LOG_FILE_ENTRY pEntry,
  732. OUT PCHAR pDestBuffer,
  733. IN OUT PULONG pBytesCopied
  734. )
  735. {
  736. NTSTATUS Status = STATUS_SUCCESS;
  737. PCHAR TitleBuffer;
  738. LONG BytesCopied;
  739. ULONG LogExtFileFlags;
  740. TIME_FIELDS CurrentTimeFields;
  741. LARGE_INTEGER CurrentTimeStamp;
  742. PUL_LOG_FILE_BUFFER pLogBuffer;
  743. PAGED_CODE();
  744. ASSERT(IS_VALID_LOG_FILE_ENTRY(pEntry));
  745. ASSERT(pEntry->Format == HttpLoggingTypeW3C);
  746. pLogBuffer = pEntry->LogBuffer;
  747. LogExtFileFlags = pEntry->LogExtFileFlags;
  748. KeQuerySystemTime(&CurrentTimeStamp);
  749. RtlTimeToTimeFields(&CurrentTimeStamp, &CurrentTimeFields);
  750. if (pDestBuffer)
  751. {
  752. // Append to the provided buffer
  753. ASSERT(pBytesCopied);
  754. ASSERT(*pBytesCopied >= UL_MAX_TITLE_BUFFER_SIZE);
  755. UlTrace(LOGGING,("Ul!UlpAppendW3CLogTitle: Copying to Provided Buffer %p\n",
  756. pDestBuffer));
  757. TitleBuffer = pDestBuffer;
  758. }
  759. else
  760. {
  761. // Append to the entry buffer
  762. ASSERT(pLogBuffer);
  763. ASSERT(pLogBuffer->Buffer);
  764. UlTrace(LOGGING,("Ul!UlpAppendW3CLogTitle: Copying to Entry Buffer %p\n",
  765. pLogBuffer));
  766. TitleBuffer = (PCHAR) pLogBuffer->Buffer + pLogBuffer->BufferUsed;
  767. }
  768. BytesCopied = _snprintf(
  769. TitleBuffer,
  770. UL_MAX_TITLE_BUFFER_SIZE,
  771. // TODO: Make this maintainance friendly
  772. "#Software: Microsoft Internet Information Services 6.0\r\n"
  773. "#Version: 1.0\r\n"
  774. "#Date: %4d-%02d-%02d %02d:%02d:%02d\r\n"
  775. "#Fields:%ls%ls%ls%ls%ls%ls%ls%ls%ls%ls%ls%ls%ls%ls%ls%ls%ls%ls%ls%ls%ls \r\n",
  776. CurrentTimeFields.Year,
  777. CurrentTimeFields.Month,
  778. CurrentTimeFields.Day,
  779. CurrentTimeFields.Hour,
  780. CurrentTimeFields.Minute,
  781. CurrentTimeFields.Second,
  782. UL_GET_LOG_TITLE_IF_PICKED(UlLogFieldDate,LogExtFileFlags,MD_EXTLOG_DATE),
  783. UL_GET_LOG_TITLE_IF_PICKED(UlLogFieldTime,LogExtFileFlags,MD_EXTLOG_TIME),
  784. UL_GET_LOG_TITLE_IF_PICKED(UlLogFieldSiteName,LogExtFileFlags,MD_EXTLOG_SITE_NAME),
  785. UL_GET_LOG_TITLE_IF_PICKED(UlLogFieldServerName,LogExtFileFlags,MD_EXTLOG_COMPUTER_NAME),
  786. UL_GET_LOG_TITLE_IF_PICKED(UlLogFieldServerIp,LogExtFileFlags,MD_EXTLOG_SERVER_IP),
  787. UL_GET_LOG_TITLE_IF_PICKED(UlLogFieldMethod,LogExtFileFlags,MD_EXTLOG_METHOD),
  788. UL_GET_LOG_TITLE_IF_PICKED(UlLogFieldUriStem,LogExtFileFlags,MD_EXTLOG_URI_STEM),
  789. UL_GET_LOG_TITLE_IF_PICKED(UlLogFieldUriQuery,LogExtFileFlags,MD_EXTLOG_URI_QUERY),
  790. UL_GET_LOG_TITLE_IF_PICKED(UlLogFieldProtocolStatus,LogExtFileFlags,MD_EXTLOG_HTTP_STATUS),
  791. UL_GET_LOG_TITLE_IF_PICKED(UlLogFieldWin32Status,LogExtFileFlags,MD_EXTLOG_WIN32_STATUS),
  792. UL_GET_LOG_TITLE_IF_PICKED(UlLogFieldServerPort,LogExtFileFlags,MD_EXTLOG_SERVER_PORT),
  793. UL_GET_LOG_TITLE_IF_PICKED(UlLogFieldUserName,LogExtFileFlags,MD_EXTLOG_USERNAME),
  794. UL_GET_LOG_TITLE_IF_PICKED(UlLogFieldClientIp,LogExtFileFlags,MD_EXTLOG_CLIENT_IP),
  795. UL_GET_LOG_TITLE_IF_PICKED(UlLogFieldProtocolVersion,LogExtFileFlags,MD_EXTLOG_PROTOCOL_VERSION),
  796. UL_GET_LOG_TITLE_IF_PICKED(UlLogFieldUserAgent,LogExtFileFlags,MD_EXTLOG_USER_AGENT),
  797. UL_GET_LOG_TITLE_IF_PICKED(UlLogFieldCookie,LogExtFileFlags,MD_EXTLOG_COOKIE),
  798. UL_GET_LOG_TITLE_IF_PICKED(UlLogFieldReferrer,LogExtFileFlags,MD_EXTLOG_REFERER),
  799. UL_GET_LOG_TITLE_IF_PICKED(UlLogFieldHost,LogExtFileFlags,MD_EXTLOG_HOST),
  800. UL_GET_LOG_TITLE_IF_PICKED(UlLogFieldBytesSent,LogExtFileFlags,MD_EXTLOG_BYTES_SENT),
  801. UL_GET_LOG_TITLE_IF_PICKED(UlLogFieldBytesReceived,LogExtFileFlags,MD_EXTLOG_BYTES_RECV),
  802. UL_GET_LOG_TITLE_IF_PICKED(UlLogFieldTimeTaken,LogExtFileFlags,MD_EXTLOG_TIME_TAKEN)
  803. );
  804. if (BytesCopied < 0)
  805. {
  806. ASSERT(!"Default title buffer size is too small !");
  807. BytesCopied = UL_MAX_TITLE_BUFFER_SIZE;
  808. }
  809. if (pDestBuffer)
  810. {
  811. *pBytesCopied = BytesCopied;
  812. }
  813. else
  814. {
  815. pLogBuffer->BufferUsed += BytesCopied;
  816. UlpIncrementBytesWritten(pEntry, BytesCopied);
  817. }
  818. return STATUS_SUCCESS;
  819. }
  820. /***************************************************************************++
  821. Routine Description:
  822. Writes a record to the log buffer and flushes.
  823. This func only get called when debug parameter
  824. g_UlDebugLogBufferPeriod is set.
  825. REQUIRES you to hold the entry eresource EXCLUSIVE.
  826. Arguments:
  827. pFile - Handle to a log file entry
  828. RecordSize - Length of the record to be written.
  829. --***************************************************************************/
  830. NTSTATUS
  831. UlpWriteToLogFileDebug(
  832. IN PUL_LOG_FILE_ENTRY pFile,
  833. IN ULONG RecordSize,
  834. IN PCHAR pRecord,
  835. IN ULONG UsedOffset1,
  836. IN ULONG UsedOffset2
  837. )
  838. {
  839. NTSTATUS Status = STATUS_SUCCESS;
  840. PUL_LOG_FILE_BUFFER pLogBuffer;
  841. ULONG RecordSizePlusTitle = RecordSize;
  842. CHAR TitleBuffer[UL_MAX_TITLE_BUFFER_SIZE];
  843. ULONG TitleBufferSize = UL_MAX_TITLE_BUFFER_SIZE;
  844. PAGED_CODE();
  845. ASSERT(IS_VALID_LOG_FILE_ENTRY(pFile));
  846. ASSERT(UlDbgResourceOwnedExclusive(&pFile->EntryResource));
  847. ASSERT(g_UlDebugLogBufferPeriod!=0);
  848. UlTrace(LOGGING,("Ul!UlpWriteToLogFileDebug: pEntry %p\n", pFile ));
  849. if (!pFile->Flags.LogTitleWritten)
  850. {
  851. // First append to the temp buffer to calculate the size
  852. UlpAppendW3CLogTitle(pFile, TitleBuffer, &TitleBufferSize);
  853. RecordSizePlusTitle += TitleBufferSize;
  854. }
  855. if (UlpIsLogFileOverFlow(pFile,RecordSizePlusTitle))
  856. {
  857. Status = UlpRecycleLogFile(pFile);
  858. }
  859. if (pFile->hFile==NULL || !NT_SUCCESS(Status))
  860. {
  861. //
  862. // If we were unable to acquire a new file handle that means logging
  863. // is temporarly ceased because of either STATUS_DISK_FULL or the
  864. // drive went down for some reason. We just bail out.
  865. //
  866. return Status;
  867. }
  868. if (!pFile->LogBuffer)
  869. {
  870. //
  871. // The buffer will be null for each log hit when log buffering
  872. // is disabled.
  873. //
  874. pFile->LogBuffer = UlPplAllocateLogBuffer();
  875. if (!pFile->LogBuffer)
  876. {
  877. return STATUS_NO_MEMORY;
  878. }
  879. }
  880. pLogBuffer = pFile->LogBuffer;
  881. ASSERT(pLogBuffer->BufferUsed == 0);
  882. if (!pFile->Flags.LogTitleWritten)
  883. {
  884. UlpAppendW3CLogTitle(pFile, NULL, NULL);
  885. }
  886. ASSERT(RecordSize + pLogBuffer->BufferUsed <= g_UlLogBufferSize);
  887. UlpAppendToLogBuffer(
  888. pFile,
  889. pLogBuffer->BufferUsed,
  890. RecordSize,
  891. pRecord,
  892. UsedOffset1,
  893. UsedOffset2
  894. );
  895. pLogBuffer->BufferUsed += RecordSize;
  896. Status = UlpFlushLogFile(pFile);
  897. if (!NT_SUCCESS(Status))
  898. {
  899. return Status;
  900. }
  901. pFile->Flags.LogTitleWritten = 1;
  902. return STATUS_SUCCESS;
  903. }
  904. /***************************************************************************++
  905. Routine Description:
  906. UlpWriteToLogFileShared :
  907. Writes a record to a log file
  908. REQUIRES you to hold the loglist resource shared
  909. Arguments:
  910. pFile - Handle to a log file entry
  911. RecordSize - Length of the record to be written.
  912. pRecord - The log record to be written to the log buffer
  913. --***************************************************************************/
  914. NTSTATUS
  915. UlpWriteToLogFileShared(
  916. IN PUL_LOG_FILE_ENTRY pFile,
  917. IN ULONG RecordSize,
  918. IN PCHAR pRecord,
  919. IN ULONG UsedOffset1,
  920. IN ULONG UsedOffset2
  921. )
  922. {
  923. PUL_LOG_FILE_BUFFER pLogBuffer;
  924. LONG BufferUsed;
  925. PAGED_CODE();
  926. ASSERT(IS_VALID_LOG_FILE_ENTRY(pFile));
  927. ASSERT(g_UlDebugLogBufferPeriod== 0);
  928. pLogBuffer = pFile->LogBuffer;
  929. UlTrace(LOGGING,("Ul!UlpWriteToLogFileShared: pEntry %p\n", pFile));
  930. //
  931. // Bail out and try the exclusive writer for cases;
  932. //
  933. // 1. No log buffer available.
  934. // 2. Logging ceased. (NULL handle)
  935. // 3. Title needs to be written.
  936. // 4. The actual log file itself has to be recycled.
  937. //
  938. // Otherwise proceed with appending to the current buffer
  939. // if there is enough space avialable for us. If not;
  940. //
  941. // 5. Bail out to get a new buffer
  942. //
  943. if ( pLogBuffer==NULL ||
  944. pFile->hFile==NULL ||
  945. !pFile->Flags.LogTitleWritten ||
  946. UlpIsLogFileOverFlow(pFile,RecordSize)
  947. )
  948. {
  949. return STATUS_MORE_PROCESSING_REQUIRED;
  950. }
  951. //
  952. // Reserve space in pLogBuffer by InterlockedCompareExchange add
  953. // RecordSize. If we exceed the limit, bail out and take the
  954. // exclusive lock to flush the buffer.
  955. //
  956. do
  957. {
  958. BufferUsed = *((volatile LONG *) &pLogBuffer->BufferUsed);
  959. if ( RecordSize + BufferUsed > g_UlLogBufferSize )
  960. {
  961. return STATUS_MORE_PROCESSING_REQUIRED;
  962. }
  963. } while (BufferUsed != InterlockedCompareExchange(
  964. &pLogBuffer->BufferUsed,
  965. RecordSize + BufferUsed,
  966. BufferUsed
  967. ));
  968. //
  969. // Keep buffering untill our buffer is full.
  970. //
  971. UlpAppendToLogBuffer(
  972. pFile,
  973. BufferUsed,
  974. RecordSize,
  975. pRecord,
  976. UsedOffset1,
  977. UsedOffset2
  978. );
  979. return STATUS_SUCCESS;
  980. }
  981. /***************************************************************************++
  982. Routine Description:
  983. By assuming that it's holding the entrie's eresource exclusively
  984. this function does various functions;
  985. - It Writes a record to a log file
  986. REQUIRES you to hold the loglist resource shared
  987. Arguments:
  988. pFile - Handle to a log file entry
  989. RecordSize - Length of the record to be written.
  990. --***************************************************************************/
  991. NTSTATUS
  992. UlpWriteToLogFileExclusive(
  993. IN PUL_LOG_FILE_ENTRY pFile,
  994. IN ULONG RecordSize,
  995. IN PCHAR pRecord,
  996. IN ULONG UsedOffset1,
  997. IN ULONG UsedOffset2
  998. )
  999. {
  1000. PUL_LOG_FILE_BUFFER pLogBuffer;
  1001. NTSTATUS Status = STATUS_SUCCESS;
  1002. ULONG RecordSizePlusTitle = RecordSize;
  1003. CHAR TitleBuffer[UL_MAX_TITLE_BUFFER_SIZE];
  1004. ULONG TitleBufferSize = UL_MAX_TITLE_BUFFER_SIZE;
  1005. PAGED_CODE();
  1006. ASSERT(IS_VALID_LOG_FILE_ENTRY(pFile));
  1007. ASSERT(g_UlDebugLogBufferPeriod== 0);
  1008. ASSERT(UlDbgResourceOwnedExclusive(&pFile->EntryResource));
  1009. UlTrace(LOGGING,("Ul!UlpWriteToLogFileExclusive: pEntry %p\n", pFile));
  1010. //
  1011. // First append title to the temp buffer to calculate the size of
  1012. // the title if we need to write the title as well.
  1013. //
  1014. if (!pFile->Flags.LogTitleWritten)
  1015. {
  1016. UlpAppendW3CLogTitle(pFile, TitleBuffer, &TitleBufferSize);
  1017. RecordSizePlusTitle += TitleBufferSize;
  1018. }
  1019. //
  1020. // Now check log file overflow.
  1021. //
  1022. if (UlpIsLogFileOverFlow(pFile,RecordSizePlusTitle))
  1023. {
  1024. //
  1025. // We already acquired the LogListResource Shared and the
  1026. // entry eresource exclusive. Therefore ReCycle is fine. Look
  1027. // at the comment in UlpWriteToLogFile.
  1028. //
  1029. Status = UlpRecycleLogFile(pFile);
  1030. }
  1031. if (pFile->hFile==NULL || !NT_SUCCESS(Status))
  1032. {
  1033. //
  1034. // If somehow the logging ceased and handle released,it happens
  1035. // when recycle isn't able to write to the log drive.
  1036. //
  1037. return Status;
  1038. }
  1039. pLogBuffer = pFile->LogBuffer;
  1040. if (pLogBuffer)
  1041. {
  1042. //
  1043. // There are two conditions we execute the following if block
  1044. // 1. We were blocked on eresource exclusive and before us some
  1045. // other thread already take care of the buffer flush or recycling.
  1046. // 2. Reconfiguration happened and log attempt needs to write the
  1047. // title again.
  1048. //
  1049. if (RecordSizePlusTitle + pLogBuffer->BufferUsed <= g_UlLogBufferSize)
  1050. {
  1051. //
  1052. // If this is the first log attempt after a reconfig, then we have
  1053. // to write the title here. Reconfig doesn't immediately write the
  1054. // title but rather depend on us by setting the LogTitleWritten flag
  1055. // to false.
  1056. //
  1057. if (!pFile->Flags.LogTitleWritten)
  1058. {
  1059. ASSERT(RecordSizePlusTitle > RecordSize);
  1060. UlpAppendW3CLogTitle(pFile, NULL, NULL);
  1061. pFile->Flags.LogTitleWritten = 1;
  1062. }
  1063. UlpAppendToLogBuffer(
  1064. pFile,
  1065. pLogBuffer->BufferUsed,
  1066. RecordSize,
  1067. pRecord,
  1068. UsedOffset1,
  1069. UsedOffset2
  1070. );
  1071. pLogBuffer->BufferUsed += RecordSize;
  1072. return STATUS_SUCCESS;
  1073. }
  1074. //
  1075. // Flush out the buffer first then proceed with allocating a new one.
  1076. //
  1077. Status = UlpFlushLogFile(pFile);
  1078. if (!NT_SUCCESS(Status))
  1079. {
  1080. return Status;
  1081. }
  1082. }
  1083. ASSERT(pFile->LogBuffer == NULL);
  1084. //
  1085. // This can be the very first log attempt or the previous allocation
  1086. // of LogBuffer failed, or the previous hit flushed and deallocated
  1087. // the old buffer. In either case, we allocate a new one,append the
  1088. // (title plus) new record and return for more/shared processing.
  1089. //
  1090. pLogBuffer = pFile->LogBuffer = UlPplAllocateLogBuffer();
  1091. if (pLogBuffer == NULL)
  1092. {
  1093. return STATUS_NO_MEMORY;
  1094. }
  1095. //
  1096. // Very first attempt needs to write the title, as well as the attempt
  1097. // which causes the log file recycling. Both cases comes down here
  1098. //
  1099. if (!pFile->Flags.LogTitleWritten)
  1100. {
  1101. UlpAppendW3CLogTitle(pFile, NULL, NULL);
  1102. pFile->Flags.LogTitleWritten = 1;
  1103. }
  1104. UlpAppendToLogBuffer(
  1105. pFile,
  1106. pLogBuffer->BufferUsed,
  1107. RecordSize,
  1108. pRecord,
  1109. UsedOffset1,
  1110. UsedOffset2
  1111. );
  1112. pLogBuffer->BufferUsed += RecordSize;
  1113. return STATUS_SUCCESS;
  1114. }
  1115. /***************************************************************************++
  1116. Routine Description:
  1117. UlpQueryDirectory:
  1118. * What file should IIS write to when logging type is daily/weekly/monthly/
  1119. hourly if there is already a log file there for that day?
  1120. IIS should write to the current day/week/month/hour's log file. For
  1121. example, let's say there's an extended log file in my log directory
  1122. called ex000727.log. IIS should append new log entries to this log,
  1123. as it is for today.
  1124. * What file should IIS write to when logging type is MAXSIZE when there are
  1125. already log files there for maxsize (like extend0.log, extend1.log, etc.)?
  1126. IIS should write to the max extend#.log file, where max(extend#.log)
  1127. is has the largest # in the #field for extend#.log. This is provided,
  1128. of course, that the MAXSIZE in that file hasn't been exceeded.
  1129. * This function quite similar to the implementation of the FindFirstFile
  1130. Win32 API. Except that it has been shaped to our purposes.
  1131. Arguments:
  1132. pEntry - The log file entry which freshly created.
  1133. --***************************************************************************/
  1134. NTSTATUS
  1135. UlpQueryDirectory(
  1136. IN OUT PUL_LOG_FILE_ENTRY pEntry
  1137. )
  1138. {
  1139. #define GET_NEXT_FILE(pv, cb) \
  1140. (FILE_DIRECTORY_INFORMATION *) ((VOID *) (((UCHAR *) (pv)) + (cb)))
  1141. NTSTATUS Status = STATUS_SUCCESS;
  1142. OBJECT_ATTRIBUTES ObjectAttributes;
  1143. IO_STATUS_BLOCK IoStatusBlock;
  1144. LONG WcharsCopied;
  1145. HANDLE hDirectory;
  1146. ULONG Sequence;
  1147. ULONG LastSequence;
  1148. UNICODE_STRING Temp;
  1149. PWCHAR pTemp;
  1150. UNICODE_STRING FileName;
  1151. WCHAR _FileName[UL_MAX_FILE_NAME_SUFFIX_LENGTH];
  1152. FILE_DIRECTORY_INFORMATION *pFdi;
  1153. PUCHAR FileInfoBuffer;
  1154. ULARGE_INTEGER FileSize;
  1155. WCHAR OriginalWChar;
  1156. PAGED_CODE();
  1157. Status = STATUS_SUCCESS;
  1158. hDirectory = NULL;
  1159. FileInfoBuffer = NULL;
  1160. ASSERT(IS_VALID_LOG_FILE_ENTRY(pEntry));
  1161. UlTrace(LOGGING,("Ul!UlpQueryDirectory: %S\n",pEntry->FileName.Buffer));
  1162. ASSERT(pEntry->Period == HttpLoggingPeriodMaxSize);
  1163. ASSERT(UL_DIRECTORY_SEARCH_BUFFER_SIZE >=
  1164. (sizeof(FILE_DIRECTORY_INFORMATION)+UL_MAX_FILE_NAME_SUFFIX_LENGTH));
  1165. //
  1166. // Open the directory for the list access again. Use the filename in
  1167. // pEntry. Where pShortName points to the "\inetsv1.log" portion of
  1168. // the whole "\??\c:\whistler\system32\logfiles\w3svc1\inetsv1.log"
  1169. // Overwrite the pShortName to get the directory name. Once we are
  1170. // done with finding the last sequence we will restore it back later
  1171. // on.
  1172. //
  1173. OriginalWChar = *((PWCHAR)pEntry->pShortName);
  1174. *((PWCHAR)pEntry->pShortName) = UNICODE_NULL;
  1175. pEntry->FileName.Length =
  1176. wcslen(pEntry->FileName.Buffer) * sizeof(WCHAR);
  1177. InitializeObjectAttributes(
  1178. &ObjectAttributes,
  1179. &pEntry->FileName,
  1180. OBJ_CASE_INSENSITIVE|UL_KERNEL_HANDLE,
  1181. NULL,
  1182. NULL
  1183. );
  1184. Status = ZwCreateFile(
  1185. &hDirectory,
  1186. SYNCHRONIZE|FILE_LIST_DIRECTORY,
  1187. &ObjectAttributes,
  1188. &IoStatusBlock,
  1189. NULL,
  1190. FILE_ATTRIBUTE_NORMAL,
  1191. FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
  1192. FILE_OPEN,
  1193. FILE_SYNCHRONOUS_IO_NONALERT|FILE_DIRECTORY_FILE,
  1194. NULL,
  1195. 0
  1196. );
  1197. if (!NT_SUCCESS(Status))
  1198. {
  1199. //
  1200. // This call should never fail since CreateLog already created
  1201. // the directory for us.
  1202. //
  1203. ASSERT(!"Directory Invalid!\n");
  1204. goto end;
  1205. }
  1206. //
  1207. // Before querrying we need to provide additional DOS-like wildcard
  1208. // matching semantics. In our case, only * to DOS_STAR conversion is
  1209. // enough though. The following is the pattern we will use for query
  1210. // Skipping the first slash character.
  1211. //
  1212. FileName.Buffer = &_FileName[1];
  1213. WcharsCopied = _snwprintf( _FileName,
  1214. UL_MAX_FILE_NAME_SUFFIX_LENGTH,
  1215. L"%s%c.%s",
  1216. UL_GET_LOG_FILE_NAME_PREFIX(pEntry->Format),
  1217. DOS_STAR,
  1218. DEFAULT_LOG_FILE_EXTENSION
  1219. );
  1220. ASSERT(WcharsCopied > 0);
  1221. FileName.Length = wcslen(FileName.Buffer) * sizeof(WCHAR);
  1222. FileName.MaximumLength = FileName.Length;
  1223. //
  1224. // This non-paged buffer should be allocated to be used for storing
  1225. // query results.
  1226. //
  1227. FileInfoBuffer =
  1228. UL_ALLOCATE_ARRAY(
  1229. NonPagedPool,
  1230. UCHAR,
  1231. UL_DIRECTORY_SEARCH_BUFFER_SIZE + sizeof(WCHAR),
  1232. UL_LOG_GENERIC_POOL_TAG
  1233. );
  1234. if (FileInfoBuffer == NULL)
  1235. {
  1236. Status = STATUS_NO_MEMORY;
  1237. goto end;
  1238. }
  1239. //
  1240. // The very first call may also fail if there is no log file in the
  1241. // current directory.
  1242. //
  1243. Status = ZwQueryDirectoryFile (
  1244. hDirectory,
  1245. NULL,
  1246. NULL,
  1247. NULL,
  1248. &IoStatusBlock,
  1249. FileInfoBuffer,
  1250. UL_DIRECTORY_SEARCH_BUFFER_SIZE,
  1251. FileDirectoryInformation,
  1252. FALSE,
  1253. &FileName,
  1254. TRUE
  1255. );
  1256. if(!NT_SUCCESS(Status))
  1257. {
  1258. //
  1259. // This should never fail with STATUS_BUFFER_OVERFLOW unless the
  1260. // buffer size is ridiculously small i.e. 50 bytes or something
  1261. //
  1262. UlTrace( LOGGING,
  1263. ("Ul!UlpQueryDirectory: Status %08lx for %S & %S\n",
  1264. Status,
  1265. pEntry->FileName.Buffer,
  1266. FileName.Buffer
  1267. ));
  1268. ASSERT(Status == STATUS_NO_SUCH_FILE);
  1269. Status = STATUS_SUCCESS;
  1270. goto end;
  1271. }
  1272. //
  1273. // Look into the buffer and get the sequence number from filename.
  1274. //
  1275. pFdi = (FILE_DIRECTORY_INFORMATION *) FileInfoBuffer;
  1276. Sequence = LastSequence = 1;
  1277. FileSize.QuadPart = 0;
  1278. while (TRUE)
  1279. {
  1280. //
  1281. // Get the latest Sequence Number from the filename
  1282. //
  1283. if (pTemp = wcsstr(pFdi->FileName,DEFAULT_LOG_FILE_EXTENSION_PLUS_DOT))
  1284. {
  1285. *pTemp = UNICODE_NULL;
  1286. pTemp = pFdi->FileName;
  1287. while ( *pTemp != UNICODE_NULL )
  1288. {
  1289. if ( isdigit((CHAR) (*pTemp)) )
  1290. {
  1291. Temp.Length = wcslen(pTemp) * sizeof(WCHAR);
  1292. Temp.MaximumLength = Temp.Length;
  1293. Temp.Buffer = pTemp;
  1294. RtlUnicodeStringToInteger( &Temp, 10, &LastSequence );
  1295. break;
  1296. }
  1297. pTemp++;
  1298. }
  1299. }
  1300. else
  1301. {
  1302. ASSERT(FALSE);
  1303. }
  1304. if (LastSequence >= Sequence)
  1305. {
  1306. //
  1307. // Bingo ! We have two things to remember though; the file size
  1308. // and the sequence number. Cryptic it's that we are getting the
  1309. // file size from EOF. Its greater than or equal because we want
  1310. // to initialize the FileSize properly even if there's only one
  1311. // match.
  1312. //
  1313. Sequence = LastSequence;
  1314. //
  1315. // BUGBUG: The HighPart is zero unless the file size is greater
  1316. // than the MAXDWORD. If so the file size calculated as in the
  1317. // formula; ((HighPart * (MAXDWORD+1)) + LowPart)
  1318. //
  1319. FileSize.LowPart = pFdi->EndOfFile.LowPart;
  1320. }
  1321. //
  1322. // Keep going until we see no more files
  1323. //
  1324. if (pFdi->NextEntryOffset != 0)
  1325. {
  1326. //
  1327. // Search through the buffer as long as there is still something
  1328. // in there.
  1329. //
  1330. pFdi = GET_NEXT_FILE(pFdi, pFdi->NextEntryOffset);
  1331. }
  1332. else
  1333. {
  1334. //
  1335. // Otherwise query again for any other possible log file(s)
  1336. //
  1337. Status = ZwQueryDirectoryFile (
  1338. hDirectory,
  1339. NULL,
  1340. NULL,
  1341. NULL,
  1342. &IoStatusBlock,
  1343. FileInfoBuffer,
  1344. UL_DIRECTORY_SEARCH_BUFFER_SIZE,
  1345. FileDirectoryInformation,
  1346. FALSE,
  1347. NULL,
  1348. FALSE
  1349. );
  1350. if (Status == STATUS_NO_MORE_FILES)
  1351. {
  1352. Status = STATUS_SUCCESS;
  1353. break;
  1354. }
  1355. if (!NT_SUCCESS(Status))
  1356. {
  1357. goto end;
  1358. }
  1359. pFdi = (FILE_DIRECTORY_INFORMATION *) FileInfoBuffer;
  1360. }
  1361. }
  1362. //
  1363. // Construct the log file name properly from the sequence number so that
  1364. // our caller can create the log file later on.
  1365. //
  1366. WcharsCopied = _snwprintf( pEntry->pShortName,
  1367. UL_MAX_FILE_NAME_SUFFIX_LENGTH,
  1368. L"%s%d.%s",
  1369. UL_GET_LOG_FILE_NAME_PREFIX(pEntry->Format),
  1370. Sequence,
  1371. DEFAULT_LOG_FILE_EXTENSION
  1372. );
  1373. ASSERT(WcharsCopied > 0);
  1374. pEntry->FileName.Length =
  1375. wcslen(pEntry->FileName.Buffer) * sizeof(WCHAR);
  1376. //
  1377. // Set the next sequence number according to last log file
  1378. //
  1379. pEntry->SequenceNumber = Sequence + 1;
  1380. //
  1381. // Update the log file size accordingly in the entry.Otherwise truncation
  1382. // will not work properly.
  1383. //
  1384. pEntry->TotalWritten.QuadPart = FileSize.QuadPart;
  1385. UlTrace( LOGGING,
  1386. ("Ul!UlpQueryDirectory: %S has been found with size %d.\n",
  1387. pEntry->FileName.Buffer,
  1388. pEntry->TotalWritten.QuadPart
  1389. ));
  1390. end:
  1391. if (*((PWCHAR)pEntry->pShortName) == UNICODE_NULL )
  1392. {
  1393. //
  1394. // We have failed for some reason before reconstructing the filename
  1395. // Perhaps because the directory was empty. Do not forget to restore
  1396. // the pShortName in the pEntry then.
  1397. //
  1398. *((PWCHAR)pEntry->pShortName) = OriginalWChar;
  1399. pEntry->FileName.Length =
  1400. wcslen(pEntry->FileName.Buffer) * sizeof(WCHAR);
  1401. }
  1402. if (FileInfoBuffer)
  1403. {
  1404. UL_FREE_POOL( FileInfoBuffer, UL_LOG_GENERIC_POOL_TAG );
  1405. }
  1406. if (!NT_SUCCESS(Status))
  1407. {
  1408. UlTrace( LOGGING,
  1409. ("Ul!UlpQueryDirectory: failure %08lx for %S\n",
  1410. Status,
  1411. pEntry->FileName.Buffer
  1412. ));
  1413. }
  1414. if (hDirectory != NULL)
  1415. {
  1416. ZwClose(hDirectory);
  1417. }
  1418. return Status;
  1419. }
  1420. /***************************************************************************++
  1421. Routine Description:
  1422. UlCreateLog:
  1423. Creates a new Logging file and insert a corresponding entry
  1424. to the global LoggingList.
  1425. Each log file belongs to a single ConfigGroup and can be
  1426. created by one. Although we keep a list of already created log files
  1427. for convenience, each config group has a pointer to its log file.
  1428. If this function fails for any reason, file entry pointer of the
  1429. config group will set to NULL.
  1430. Arguments:
  1431. pConfigGroup - Supplies the necessary information for opening the
  1432. log file and gets the result entry pointer.
  1433. LogListResource also implicitly protects this pointer
  1434. since only possible places which are going to update
  1435. it use the resource exclusively.
  1436. --***************************************************************************/
  1437. NTSTATUS
  1438. UlCreateLog(
  1439. IN OUT PUL_CONFIG_GROUP_OBJECT pConfigGroup
  1440. )
  1441. {
  1442. NTSTATUS Status;
  1443. OBJECT_ATTRIBUTES ObjectAttributes;
  1444. IO_STATUS_BLOCK IoStatusBlock;
  1445. PUL_LOG_FILE_ENTRY pNewEntry;
  1446. TIME_FIELDS CurrentTimeFields;
  1447. LARGE_INTEGER CurrentTimeStamp;
  1448. HANDLE hDirectory;
  1449. UNICODE_STRING DirectoryName;
  1450. //
  1451. // Sanity check.
  1452. //
  1453. PAGED_CODE();
  1454. ASSERT(pConfigGroup != NULL);
  1455. UlTrace(LOGGING, ("Ul!UlCreateLog: pConfigGroup %p Truncate %d\n",
  1456. pConfigGroup, pConfigGroup->LoggingConfig.LogFileTruncateSize
  1457. ));
  1458. //
  1459. // An important check to ensure that no infinite loop occurs because of
  1460. // ridiculusly small truncatesizes. Means smaller than maximum
  1461. // allowed log record line (10*1024)
  1462. //
  1463. if ( pConfigGroup->LoggingConfig.LogPeriod == HttpLoggingPeriodMaxSize &&
  1464. pConfigGroup->LoggingConfig.LogFileTruncateSize != HTTP_LIMIT_INFINITE &&
  1465. pConfigGroup->LoggingConfig.LogFileTruncateSize < UL_MIN_ALLOWED_TRUNCATESIZE
  1466. )
  1467. {
  1468. UlTrace(LOGGING,
  1469. ("Ul!UlCreateLog: Truncate size too small pConfigGroup %p : %d\n",
  1470. pConfigGroup,
  1471. pConfigGroup->LoggingConfig.LogFileTruncateSize
  1472. ));
  1473. return STATUS_INVALID_PARAMETER;
  1474. }
  1475. //
  1476. // We have two criteria for the log file name: its LogFormat and its LogPeriod
  1477. //
  1478. if ( pConfigGroup->LoggingConfig.LogFormat >= HttpLoggingTypeMaximum ||
  1479. pConfigGroup->LoggingConfig.LogPeriod >= HttpLoggingPeriodMaximum )
  1480. {
  1481. return STATUS_INVALID_PARAMETER;
  1482. }
  1483. //
  1484. // Setup locals so we know how to cleanup on exit.
  1485. //
  1486. Status = STATUS_SUCCESS;
  1487. pNewEntry = NULL;
  1488. //
  1489. // This value is computed for the GMT time zone.
  1490. //
  1491. KeQuerySystemTime( &CurrentTimeStamp );
  1492. RtlTimeToTimeFields( &CurrentTimeStamp, &CurrentTimeFields );
  1493. //
  1494. // Allocate a temp buffer to hold the full path name including the
  1495. // device prefix and the filename at the end.
  1496. //
  1497. Status = UlpBuildLogDirectory(
  1498. &pConfigGroup->LoggingConfig.LogFileDir,
  1499. &DirectoryName
  1500. );
  1501. if (!NT_SUCCESS(Status))
  1502. {
  1503. return Status;
  1504. }
  1505. //
  1506. // We have to acquire the LogListresource exclusively, prior to
  1507. // the operations Create/Remove/ReConfig. Whenever we acquire the
  1508. // LogListResource exclusively we don't need to have the entry mutex
  1509. // acquired.
  1510. //
  1511. UlAcquireResourceExclusive(&g_pUlNonpagedData->LogListResource, TRUE);
  1512. pConfigGroup->pLogFileEntry = NULL;
  1513. Status = UlpConstructLogFileEntry (
  1514. &pConfigGroup->LoggingConfig,
  1515. &pNewEntry,
  1516. &DirectoryName,
  1517. &CurrentTimeFields
  1518. );
  1519. if (NT_SUCCESS(Status) == FALSE)
  1520. goto end;
  1521. //
  1522. // Create/Open the directory(ies) first.
  1523. //
  1524. Status = UlpCreateSafeDirectory( &DirectoryName );
  1525. if (NT_SUCCESS(Status) == FALSE)
  1526. goto end;
  1527. //
  1528. // If the logformat is max_size with truncation we need to scan the
  1529. // directory and find the correct last log file to append. Otherwise
  1530. // just picking the FILE_OPEN_IF when opening the log file will ensure
  1531. // the functionality.
  1532. //
  1533. if (pNewEntry->Period == HttpLoggingPeriodMaxSize)
  1534. {
  1535. //
  1536. // This call will update the filename and the file size
  1537. //
  1538. Status = UlpQueryDirectory(pNewEntry);
  1539. if (!NT_SUCCESS(Status))
  1540. goto end;
  1541. }
  1542. //
  1543. // Create/Open the file.
  1544. //
  1545. InitializeObjectAttributes(
  1546. &ObjectAttributes,
  1547. &pNewEntry->FileName, // Full path name
  1548. OBJ_CASE_INSENSITIVE | // Attributes
  1549. UL_KERNEL_HANDLE,
  1550. NULL, // RootDirectory
  1551. NULL // SecurityDescriptor
  1552. );
  1553. //
  1554. // Make the created file Aysnc by not picking the sync flag.
  1555. //
  1556. Status = ZwCreateFile(
  1557. &pNewEntry->hFile,
  1558. FILE_GENERIC_WRITE,
  1559. &ObjectAttributes,
  1560. &IoStatusBlock,
  1561. NULL,
  1562. FILE_ATTRIBUTE_NORMAL,
  1563. FILE_SHARE_READ,
  1564. FILE_OPEN_IF,
  1565. FILE_NON_DIRECTORY_FILE, // |FILE_SYNCHRONOUS_IO_NONALERT,
  1566. NULL,
  1567. 0);
  1568. if (NT_SUCCESS(Status) == FALSE)
  1569. goto end;
  1570. //
  1571. // Get the file size, etc from the file.
  1572. //
  1573. Status = ZwQueryInformationFile(
  1574. pNewEntry->hFile,
  1575. &IoStatusBlock,
  1576. &pNewEntry->FileInfo,
  1577. sizeof(pNewEntry->FileInfo),
  1578. FileStandardInformation
  1579. );
  1580. if (NT_SUCCESS(Status) == FALSE)
  1581. goto end;
  1582. //
  1583. // Add it to our global log list.
  1584. //
  1585. UlpInsertLogFileEntry( pNewEntry, &CurrentTimeFields );
  1586. //
  1587. // Success!
  1588. //
  1589. pConfigGroup->pLogFileEntry = pNewEntry;
  1590. UlTrace( LOGGING,
  1591. ( "Ul!UlCreateLog: entry %p, file %S, handle %lx created\n",
  1592. pNewEntry,
  1593. pNewEntry->FileName.Buffer,
  1594. pNewEntry->hFile )
  1595. );
  1596. end:
  1597. if ( !NT_SUCCESS(Status) )
  1598. {
  1599. //
  1600. // If we made it to this point, then the create/open has failed.
  1601. //
  1602. UlTrace( LOGGING,
  1603. ("Ul!UlCreateLog: dirname %S, file %S failure %08lx\n",
  1604. pConfigGroup->LoggingConfig.LogFileDir.Buffer,
  1605. pNewEntry->FileName.Buffer,
  1606. Status
  1607. ));
  1608. if (pNewEntry)
  1609. {
  1610. NTSTATUS TempStatus;
  1611. //
  1612. // Now release the entry's resources.
  1613. //
  1614. if (pNewEntry->hFile != NULL)
  1615. {
  1616. ZwClose( pNewEntry->hFile );
  1617. pNewEntry->hFile = NULL;
  1618. }
  1619. if ( pNewEntry->LogBuffer )
  1620. {
  1621. UlPplFreeLogBuffer( pNewEntry->LogBuffer );
  1622. }
  1623. // Delete the entry eresource
  1624. TempStatus = UlDeleteResource( &pNewEntry->EntryResource );
  1625. ASSERT(NT_SUCCESS(TempStatus));
  1626. UL_FREE_POOL_WITH_SIG(pNewEntry,UL_LOG_FILE_ENTRY_POOL_TAG);
  1627. }
  1628. pConfigGroup->pLogFileEntry = NULL;
  1629. }
  1630. else
  1631. {
  1632. ASSERT(pConfigGroup->pLogFileEntry != NULL);
  1633. }
  1634. // Cleanup temp dir name buffer
  1635. if (DirectoryName.Buffer)
  1636. {
  1637. UL_FREE_POOL(DirectoryName.Buffer, UL_CG_LOGDIR_POOL_TAG);
  1638. }
  1639. UlReleaseResource(&g_pUlNonpagedData->LogListResource);
  1640. return Status;
  1641. }
  1642. /***************************************************************************++
  1643. Routine Description:
  1644. UlpInsertLogFileEntry :
  1645. Inserts a log file entry to our global log entry list.
  1646. REQUIRES caller to have LogListresource EXCLUSIVELY.
  1647. Arguments:
  1648. pEntry - The log file entry to be added to the global list
  1649. pTimeFields - The current time fields.
  1650. --***************************************************************************/
  1651. VOID
  1652. UlpInsertLogFileEntry(
  1653. PUL_LOG_FILE_ENTRY pEntry,
  1654. PTIME_FIELDS pTimeFields
  1655. )
  1656. {
  1657. LONG listSize;
  1658. HTTP_LOGGING_PERIOD period;
  1659. KIRQL oldIrql;
  1660. //
  1661. // Sanity check.
  1662. //
  1663. PAGED_CODE();
  1664. ASSERT(IS_VALID_LOG_FILE_ENTRY(pEntry));
  1665. ASSERT(pTimeFields);
  1666. //
  1667. // add to the list
  1668. //
  1669. InsertHeadList(&g_LogListHead, &pEntry->LogFileListEntry);
  1670. period = pEntry->Period;
  1671. listSize = InterlockedIncrement( &g_LogListEntryCount );
  1672. ASSERT(listSize >= 1);
  1673. //
  1674. // Time to start the Log Timer if we haven't done it yet.
  1675. // Once we start this timer it keeps working until
  1676. // terminaton of the driver. CODEWORK we may start and stop it
  1677. // more intelligently, i.e. if no log requires it stop it
  1678. //
  1679. UlAcquireSpinLock( &g_LogTimerSpinLock, &oldIrql );
  1680. if ( g_LogTimerStarted == FALSE )
  1681. {
  1682. // Only if we are running on time dependent
  1683. // format
  1684. if ( period != HttpLoggingPeriodMaxSize )
  1685. {
  1686. UlpSetLogTimer( pTimeFields );
  1687. g_LogTimerStarted = TRUE;
  1688. }
  1689. }
  1690. // Go ahead start the buffer timer
  1691. // as soon as we have a log file
  1692. if ( g_BufferTimerStarted == FALSE )
  1693. {
  1694. UlpSetBufferTimer();
  1695. g_BufferTimerStarted = TRUE;
  1696. }
  1697. UlReleaseSpinLock( &g_LogTimerSpinLock, oldIrql );
  1698. }
  1699. /***************************************************************************++
  1700. Routine Description:
  1701. Simple utility to close the log file handle on a system thread and set the
  1702. event to notify the caller that it's done.
  1703. Arguments:
  1704. pEntry - Acquired from passed-in pWorkItem
  1705. --***************************************************************************/
  1706. VOID
  1707. UlpLogCloseHandleWorker(
  1708. IN PUL_WORK_ITEM pWorkItem
  1709. )
  1710. {
  1711. PUL_LOG_FILE_ENTRY pEntry;
  1712. PAGED_CODE();
  1713. // For this function to not to cause any threats to the safety of the log
  1714. // entry. The entry should already been acquired exclusively by our caller.
  1715. ASSERT(!UlDbgResourceUnownedExclusive(
  1716. &g_pUlNonpagedData->LogListResource));
  1717. // Get the log entry
  1718. pEntry = CONTAINING_RECORD(
  1719. pWorkItem,
  1720. UL_LOG_FILE_ENTRY,
  1721. WorkItem
  1722. );
  1723. ASSERT(IS_VALID_LOG_FILE_ENTRY(pEntry));
  1724. ASSERT(pEntry->hFile);
  1725. UlTrace(LOGGING,("Ul!UlpLogCloseHandleWorker: pEntry %p hFile %p\n",
  1726. pEntry, pEntry->hFile ));
  1727. // Close the handle and set the event for the original caller
  1728. ZwClose(pEntry->hFile);
  1729. pEntry->hFile = NULL;
  1730. KeSetEvent(&pEntry->CloseEvent, 0, FALSE);
  1731. }
  1732. /***************************************************************************++
  1733. Routine Description:
  1734. Simple utility to close the log file handle on a system thread and set the
  1735. event to notify the caller that it's done.
  1736. Arguments:
  1737. pEntry - Acquired from passed-in pWorkItem
  1738. --***************************************************************************/
  1739. VOID
  1740. UlpLogCloseHandle(
  1741. IN PUL_LOG_FILE_ENTRY pEntry
  1742. )
  1743. {
  1744. // Sanity check
  1745. PAGED_CODE();
  1746. ASSERT(IS_VALID_LOG_FILE_ENTRY(pEntry));
  1747. //ASSERT(UlDbgResourceOwnedExclusive(&pEntry->EntryResource));
  1748. ASSERT(g_pUlSystemProcess);
  1749. // Close the handle on the system thread and wait until it's done
  1750. if (g_pUlSystemProcess == (PKPROCESS)PsGetCurrentProcess())
  1751. {
  1752. ZwClose(pEntry->hFile);
  1753. pEntry->hFile = NULL;
  1754. }
  1755. else
  1756. {
  1757. KeAttachProcess(g_pUlSystemProcess);
  1758. ZwClose(pEntry->hFile);
  1759. pEntry->hFile = NULL;
  1760. // Following will bugcheck in the checked kernel if there's
  1761. // any user or kernel APCs attached to the thread.
  1762. KeDetachProcess();
  1763. // TODO: Find a better solution
  1764. /*
  1765. UL_QUEUE_WORK_ITEM(
  1766. &pEntry->WorkItem,
  1767. &UlpLogCloseHandleWorker
  1768. );
  1769. KeWaitForSingleObject(
  1770. (PVOID)&pEntry->CloseEvent,
  1771. UserRequest,
  1772. KernelMode,
  1773. FALSE,
  1774. NULL
  1775. );
  1776. KeClearEvent(&pEntry->CloseEvent);
  1777. */
  1778. }
  1779. }
  1780. /***************************************************************************++
  1781. Routine Description:
  1782. UlRemoveLogFileEntry :
  1783. Removes a log file entry from our global log entry list.
  1784. Arguments:
  1785. pEntry - The log file entry to be removed from the global list
  1786. --***************************************************************************/
  1787. VOID
  1788. UlRemoveLogFileEntry(
  1789. PUL_LOG_FILE_ENTRY pEntry
  1790. )
  1791. {
  1792. NTSTATUS Status;
  1793. LONG listSize;
  1794. //
  1795. // Sanity check.
  1796. //
  1797. PAGED_CODE();
  1798. ASSERT(IS_VALID_LOG_FILE_ENTRY(pEntry));
  1799. UlAcquireResourceExclusive(&g_pUlNonpagedData->LogListResource, TRUE);
  1800. RemoveEntryList(&pEntry->LogFileListEntry);
  1801. pEntry->LogFileListEntry.Flink =
  1802. pEntry->LogFileListEntry.Blink = NULL;
  1803. if (pEntry->hFile != NULL)
  1804. {
  1805. //
  1806. // Make sure that buffer get flushed out before closing the file
  1807. // handle. But flush will cause an APC to be queued to the user
  1808. // thread, therefore we have to close the handle on one of our
  1809. // system threads to avoid the possible bugcheck
  1810. // INVALID_PROCESS_DETACH_ATTEMPT condition.
  1811. //
  1812. UlpFlushLogFile(pEntry);
  1813. UlpLogCloseHandle(pEntry);
  1814. }
  1815. //
  1816. // Delete the entry eresource
  1817. //
  1818. Status = UlDeleteResource( &pEntry->EntryResource );
  1819. ASSERT(NT_SUCCESS(Status));
  1820. listSize = InterlockedDecrement( &g_LogListEntryCount );
  1821. ASSERT(listSize >= 0);
  1822. UlTrace( LOGGING,
  1823. ("Ul!UlRemoveLogFileEntry: entry %p has been removed\n",
  1824. pEntry
  1825. ));
  1826. if ( pEntry->LogBuffer )
  1827. {
  1828. UlPplFreeLogBuffer( pEntry->LogBuffer );
  1829. }
  1830. UL_FREE_POOL_WITH_SIG(pEntry,UL_LOG_FILE_ENTRY_POOL_TAG);
  1831. UlReleaseResource(&g_pUlNonpagedData->LogListResource);
  1832. }
  1833. /***************************************************************************++
  1834. Routine Description:
  1835. UlpCreateSafeDirectory :
  1836. Creates all of the necessary directories in a given UNICODE directory
  1837. pathname.
  1838. E.g. For given \??\C:\temp\w3svc1
  1839. -> Directories "C:\temp" & "C:\temp\w3svc1" will be created.
  1840. This function assumes that directory string starts with "\\??\\"
  1841. Arguments:
  1842. pDirectoryName - directroy path name string, WARNING this function makes
  1843. some inplace modification to the passed directory string
  1844. but it restores the original before returning.
  1845. Return Value:
  1846. NTSTATUS - Completion status.
  1847. --***************************************************************************/
  1848. NTSTATUS
  1849. UlpCreateSafeDirectory(
  1850. IN PUNICODE_STRING pDirectoryName
  1851. )
  1852. {
  1853. OBJECT_ATTRIBUTES ObjectAttributes;
  1854. IO_STATUS_BLOCK IoStatusBlock;
  1855. NTSTATUS Status;
  1856. HANDLE hDirectory;
  1857. PWCHAR pw;
  1858. USHORT i;
  1859. //
  1860. // Sanity check
  1861. //
  1862. PAGED_CODE();
  1863. Status = STATUS_SUCCESS;
  1864. ASSERT( pDirectoryName );
  1865. ASSERT( pDirectoryName->Buffer );
  1866. ASSERT( pDirectoryName->Length );
  1867. ASSERT( pDirectoryName->MaximumLength > pDirectoryName->Length );
  1868. pw = pDirectoryName->Buffer;
  1869. pw[pDirectoryName->Length/sizeof(WCHAR)]=UNICODE_NULL;
  1870. // TODO: Handle network mapped drives. Redirector.
  1871. if (0 == wcsncmp(pw, UL_UNC_PATH_PREFIX, UL_UNC_PATH_PREFIX_LENGTH))
  1872. {
  1873. // UNC share
  1874. pw += UL_UNC_PATH_PREFIX_LENGTH;
  1875. // Bypass "\\machine\share"
  1876. i = 0; // Skip two backslashes before reaching to share name
  1877. while( *pw != UNICODE_NULL )
  1878. {
  1879. if ( *pw == L'\\' ) i++;
  1880. if ( i == 2 ) break;
  1881. pw++;
  1882. }
  1883. }
  1884. else if (0 == wcsncmp(pw, UL_LOCAL_PATH_PREFIX, UL_LOCAL_PATH_PREFIX_LENGTH))
  1885. {
  1886. // Local Drive
  1887. pw += UL_LOCAL_PATH_PREFIX_LENGTH;
  1888. // Bypass "C:"
  1889. while( *pw != L'\\' && *pw != UNICODE_NULL )
  1890. {
  1891. pw++;
  1892. }
  1893. }
  1894. else
  1895. {
  1896. ASSERT(!"Incorrect logging directory name or type !");
  1897. return STATUS_INVALID_PARAMETER;
  1898. }
  1899. if ( *pw == UNICODE_NULL )
  1900. {
  1901. // Dir. Name cannot be only "\??\C:" or "\dosdevices\UNC\machine
  1902. // It should at least be pointing to the root directory.
  1903. ASSERT(!"Incomplete logging directory name !");
  1904. return STATUS_INVALID_PARAMETER;
  1905. }
  1906. //
  1907. // \??\C:\temp\w3svc1 OR \\dosdevices\UNC\machine\share\w3svc1
  1908. // ^ ^
  1909. // pw now points to | OR |
  1910. //
  1911. //
  1912. ASSERT( *pw == L'\\' );
  1913. do
  1914. {
  1915. pw++;
  1916. if ( *pw == L'\\' || *pw == UNICODE_NULL )
  1917. {
  1918. //
  1919. // Remember the original character
  1920. //
  1921. WCHAR wcOriginal = *pw;
  1922. UNICODE_STRING DirectoryName;
  1923. //
  1924. // Time to create the directory so far
  1925. //
  1926. *pw = UNICODE_NULL;
  1927. RtlInitUnicodeString( &DirectoryName, pDirectoryName->Buffer );
  1928. InitializeObjectAttributes(
  1929. &ObjectAttributes,
  1930. &DirectoryName,
  1931. OBJ_CASE_INSENSITIVE |
  1932. UL_KERNEL_HANDLE,
  1933. NULL,
  1934. NULL
  1935. );
  1936. Status = ZwCreateFile(
  1937. &hDirectory,
  1938. FILE_LIST_DIRECTORY|FILE_TRAVERSE,
  1939. &ObjectAttributes,
  1940. &IoStatusBlock,
  1941. NULL,
  1942. FILE_ATTRIBUTE_NORMAL,
  1943. FILE_SHARE_READ,
  1944. FILE_OPEN_IF,
  1945. FILE_DIRECTORY_FILE,
  1946. NULL,
  1947. 0
  1948. );
  1949. //
  1950. // Restore the original character
  1951. //
  1952. *pw = wcOriginal;
  1953. if (NT_SUCCESS(Status) == FALSE)
  1954. {
  1955. goto end;
  1956. }
  1957. Status = ZwClose(hDirectory);
  1958. if (NT_SUCCESS(Status) == FALSE)
  1959. {
  1960. goto end;
  1961. }
  1962. }
  1963. }
  1964. while( *pw != UNICODE_NULL );
  1965. end:
  1966. if (!NT_SUCCESS(Status))
  1967. {
  1968. UlTrace(LOGGING,
  1969. ("Ul!UlpCreateSafeDirectory: directory %S, failure %08lx\n",
  1970. pDirectoryName->Buffer,
  1971. Status
  1972. ));
  1973. }
  1974. return Status;
  1975. }
  1976. /***************************************************************************++
  1977. Routine Description:
  1978. UlpCalculateTimeToExpire :
  1979. Shamelessly stolen from IIS 5.1 Logging code and adapted here.
  1980. This routine returns the time-to-expire in hours. 1 means the log
  1981. will expire in the next timer-fire and so ...
  1982. Arguments:
  1983. PTIME_FIELDS - Current Time Fields
  1984. HTTP_LOGGING_PERIOD - Logging Period
  1985. PULONG - Pointer to a buffer to receive result
  1986. Return Value:
  1987. NTSTATUS - Completion status.
  1988. --***************************************************************************/
  1989. NTSTATUS
  1990. UlpCalculateTimeToExpire(
  1991. PTIME_FIELDS pDueTime,
  1992. HTTP_LOGGING_PERIOD LogPeriod,
  1993. PULONG pTimeRemaining
  1994. )
  1995. {
  1996. NTSTATUS Status;
  1997. ULONG NumDays;
  1998. PAGED_CODE();
  1999. ASSERT(pDueTime!=NULL);
  2000. ASSERT(pTimeRemaining!=NULL);
  2001. Status = STATUS_SUCCESS;
  2002. switch (LogPeriod)
  2003. {
  2004. case HttpLoggingPeriodMaxSize:
  2005. return Status;
  2006. case HttpLoggingPeriodHourly:
  2007. *pTimeRemaining = 1;
  2008. break;
  2009. case HttpLoggingPeriodDaily:
  2010. *pTimeRemaining = 24 - pDueTime->Hour;
  2011. break;
  2012. case HttpLoggingPeriodWeekly:
  2013. {
  2014. ULONG TimeRemainingInTheMonth;
  2015. NumDays = UlpGetMonthDays(pDueTime);
  2016. TimeRemainingInTheMonth =
  2017. NumDays*24 - ((pDueTime->Day-1)*24 + pDueTime->Hour);
  2018. // Time Remaining in the week
  2019. // Sunday = 0, Monday = 1 ... Saturday = 6
  2020. *pTimeRemaining =
  2021. 7*24 - (pDueTime->Weekday*24 + pDueTime->Hour);
  2022. //
  2023. // If the time remaining in the month less than time remaining in
  2024. // the week then we have to recycle at the end of the month.
  2025. // Otherwise we have to recycle at the end of the week. (next sunday)
  2026. //
  2027. if (TimeRemainingInTheMonth < *pTimeRemaining)
  2028. {
  2029. *pTimeRemaining = TimeRemainingInTheMonth;
  2030. }
  2031. }
  2032. break;
  2033. case HttpLoggingPeriodMonthly:
  2034. {
  2035. NumDays = UlpGetMonthDays(pDueTime);
  2036. //
  2037. // Lets not forget that the day starts from 1 .. 31
  2038. // Therefore we have to subtract one from the day value.
  2039. //
  2040. *pTimeRemaining =
  2041. NumDays*24 - ((pDueTime->Day-1)*24 + pDueTime->Hour);
  2042. }
  2043. break;
  2044. default:
  2045. ASSERT(FALSE);
  2046. return STATUS_INVALID_PARAMETER;
  2047. }
  2048. return Status;
  2049. }
  2050. /***************************************************************************++
  2051. Routine Description:
  2052. Initializes the Log & buffering Timers
  2053. --***************************************************************************/
  2054. VOID
  2055. UlpInitializeTimers(
  2056. VOID
  2057. )
  2058. {
  2059. /* Log timer */
  2060. g_LogTimerInitialized = TRUE;
  2061. UlInitializeSpinLock( &g_LogTimerSpinLock, "g_LogTimerSpinLock" );
  2062. KeInitializeDpc(
  2063. &g_LogTimerDpcObject, // DPC object
  2064. &UlLogTimerDpcRoutine, // DPC routine
  2065. NULL // context
  2066. );
  2067. KeInitializeTimer( &g_LogTimer );
  2068. /* Buffer timer */
  2069. g_BufferTimerInitialized = TRUE;
  2070. UlInitializeSpinLock( &g_BufferTimerSpinLock, "g_BufferTimerSpinLock" );
  2071. KeInitializeDpc(
  2072. &g_BufferTimerDpcObject, // DPC object
  2073. &UlBufferTimerDpcRoutine, // DPC routine
  2074. NULL // context
  2075. );
  2076. KeInitializeTimer( &g_BufferTimer );
  2077. }
  2078. /***************************************************************************++
  2079. Routine Description:
  2080. Terminates the Log & buffering Timers
  2081. --***************************************************************************/
  2082. VOID
  2083. UlpTerminateTimers(
  2084. VOID
  2085. )
  2086. {
  2087. KIRQL oldIrql;
  2088. /* Log timer */
  2089. if ( g_LogTimerInitialized )
  2090. {
  2091. UlAcquireSpinLock( &g_LogTimerSpinLock, &oldIrql );
  2092. g_LogTimerInitialized = FALSE;
  2093. UlReleaseSpinLock( &g_LogTimerSpinLock, oldIrql );
  2094. KeCancelTimer( &g_LogTimer );
  2095. }
  2096. /* Buffer timer */
  2097. if ( g_BufferTimerInitialized )
  2098. {
  2099. UlAcquireSpinLock( &g_BufferTimerSpinLock, &oldIrql );
  2100. g_BufferTimerInitialized = FALSE;
  2101. UlReleaseSpinLock( &g_BufferTimerSpinLock, oldIrql );
  2102. KeCancelTimer( &g_BufferTimer );
  2103. }
  2104. }
  2105. /***************************************************************************++
  2106. Routine Description:
  2107. UlpSetTimer :
  2108. This routine provides the initial due time for the upcoming
  2109. periodic hourly timer. We have to align the timer so that it
  2110. get signaled at the beginning of each hour. Then it goes with
  2111. an hour period until stops.
  2112. We keep ONLY one timer for all log periods. A DPC routine will
  2113. get called every hour, and it will traverse the log list and
  2114. do the cycling properly.
  2115. Arguments:
  2116. PTIME_FIELDS - Current Time
  2117. Return Value:
  2118. NTSTATUS - Completion status.
  2119. --***************************************************************************/
  2120. VOID
  2121. UlpSetLogTimer(
  2122. PTIME_FIELDS pFields
  2123. )
  2124. {
  2125. LONGLONG InitialDueTime100Ns;
  2126. LARGE_INTEGER InitialDueTime;
  2127. ASSERT(pFields!=NULL);
  2128. //
  2129. // Remaining time to next hour tick. In seconds
  2130. //
  2131. InitialDueTime100Ns =
  2132. 1*60*60 - ( pFields->Minute*60 + pFields->Second );
  2133. //
  2134. // Also convert it to 100-ns
  2135. //
  2136. InitialDueTime100Ns =
  2137. (InitialDueTime100Ns*1000 - pFields->Milliseconds ) * 1000 * 10;
  2138. //
  2139. // Negative time for relative value.
  2140. //
  2141. InitialDueTime.QuadPart = -InitialDueTime100Ns;
  2142. KeSetTimerEx(
  2143. &g_LogTimer,
  2144. InitialDueTime,
  2145. 1*60*60*1000, // Period of 1 Hours in millisec
  2146. &g_LogTimerDpcObject
  2147. );
  2148. }
  2149. /***************************************************************************++
  2150. Routine Description:
  2151. UlpSetBufferTimer :
  2152. We have to introduce a new timer for the log buffering mechanism.
  2153. Each log file keeps a system default (64K) buffer do not flush this
  2154. out unless it's full or this timer get fired every MINUTE.
  2155. The hourly timer get aligned for the beginning of each hour. Therefore
  2156. using that existing timer would introduce alot of complexity.
  2157. Arguments:
  2158. PTIME_FIELDS - Current Time
  2159. --***************************************************************************/
  2160. VOID
  2161. UlpSetBufferTimer(
  2162. VOID
  2163. )
  2164. {
  2165. LONGLONG BufferPeriodTime100Ns;
  2166. LONG BufferPeriodTimeMs;
  2167. LARGE_INTEGER BufferPeriodTime;
  2168. //
  2169. // Remaining time to next tick. DEFAULT_BUFFER_TIMER_PERIOD is in minutes
  2170. //
  2171. BufferPeriodTimeMs = DEFAULT_BUFFER_TIMER_PERIOD * 60 * 1000;
  2172. BufferPeriodTime100Ns = (LONGLONG) BufferPeriodTimeMs * 10 * 1000;
  2173. UlTrace(LOGGING,
  2174. ("Ul!UlpSetBufferTimer: %d seconds\n",
  2175. BufferPeriodTimeMs / 1000
  2176. ));
  2177. //
  2178. // Negative time for relative value.
  2179. //
  2180. BufferPeriodTime.QuadPart = -BufferPeriodTime100Ns;
  2181. KeSetTimerEx(
  2182. &g_BufferTimer,
  2183. BufferPeriodTime, // Must be in nanosec
  2184. BufferPeriodTimeMs, // Must be in millisec
  2185. &g_BufferTimerDpcObject
  2186. );
  2187. }
  2188. /***************************************************************************++
  2189. Routine Description:
  2190. UlLogTimerHandler :
  2191. Work item for the threadpool that goes thru the log list and
  2192. cycle the necessary logs.
  2193. Arguments:
  2194. PUL_WORK_ITEM - Ignored
  2195. Return Value:
  2196. None
  2197. --***************************************************************************/
  2198. VOID
  2199. UlLogTimerHandler(
  2200. IN PUL_WORK_ITEM pWorkItem
  2201. )
  2202. {
  2203. NTSTATUS Status;
  2204. PLIST_ENTRY pLink;
  2205. PUL_LOG_FILE_ENTRY pEntry;
  2206. PAGED_CODE();
  2207. UlTrace(LOGGING,("Ul!UlLogTimerHandler: Scanning the log entries\n"));
  2208. UlAcquireResourceExclusive(&g_pUlNonpagedData->LogListResource, TRUE);
  2209. // Attempt to reinit the GMT offset every hour, to pickup the changes
  2210. // because of the day light changes. Synced by the logging eresource.
  2211. UlpGetGMTOffset();
  2212. for (pLink = g_LogListHead.Flink;
  2213. pLink != &g_LogListHead;
  2214. pLink = pLink->Flink
  2215. )
  2216. {
  2217. pEntry = CONTAINING_RECORD(
  2218. pLink,
  2219. UL_LOG_FILE_ENTRY,
  2220. LogFileListEntry
  2221. );
  2222. //
  2223. // We should not recycle this entry if it's period
  2224. // is not time based but size based.
  2225. //
  2226. UlAcquireResourceExclusive(&pEntry->EntryResource, TRUE);
  2227. if (pEntry->Period != HttpLoggingPeriodMaxSize)
  2228. {
  2229. if (pEntry->TimeToExpire==1)
  2230. {
  2231. // TODO: Don't recycle if the entries logging is disabled.
  2232. SET_TIME_TO_EXPIRE_STALE(pEntry);
  2233. Status = UlpRecycleLogFile(pEntry);
  2234. }
  2235. else
  2236. {
  2237. //
  2238. // Just decrement the hourly counter
  2239. // for this time.
  2240. //
  2241. pEntry->TimeToExpire -= 1;
  2242. }
  2243. }
  2244. UlReleaseResource(&pEntry->EntryResource);
  2245. }
  2246. UlReleaseResource(&g_pUlNonpagedData->LogListResource);
  2247. //
  2248. // Free the memory allocated (ByDpcRoutine below) to
  2249. // this work item.
  2250. //
  2251. UL_FREE_POOL( pWorkItem, UL_WORK_ITEM_POOL_TAG );
  2252. }
  2253. /***************************************************************************++
  2254. Routine Description:
  2255. UlLogTimerDpcRoutine :
  2256. Arguments:
  2257. Ignored
  2258. Return Value:
  2259. NTSTATUS - Completion status.
  2260. --***************************************************************************/
  2261. VOID
  2262. UlLogTimerDpcRoutine(
  2263. PKDPC Dpc,
  2264. PVOID DeferredContext,
  2265. PVOID SystemArgument1,
  2266. PVOID SystemArgument2
  2267. )
  2268. {
  2269. PUL_WORK_ITEM pWorkItem;
  2270. UlAcquireSpinLockAtDpcLevel(
  2271. &g_LogTimerSpinLock
  2272. );
  2273. if( g_LogTimerInitialized )
  2274. {
  2275. //
  2276. // It's not possible to acquire the resource which protects
  2277. // the log list at DISPATCH_LEVEL therefore we will queue a
  2278. // work item for this.
  2279. //
  2280. pWorkItem = (PUL_WORK_ITEM) UL_ALLOCATE_POOL(
  2281. NonPagedPool,
  2282. sizeof(*pWorkItem),
  2283. UL_WORK_ITEM_POOL_TAG
  2284. );
  2285. if ( pWorkItem )
  2286. {
  2287. UL_QUEUE_WORK_ITEM(
  2288. pWorkItem,
  2289. &UlLogTimerHandler
  2290. );
  2291. }
  2292. else
  2293. {
  2294. UlTrace(LOGGING,("Ul!UlLogTimerDpcRoutine: Not enough memory.\n"));
  2295. }
  2296. }
  2297. UlReleaseSpinLockFromDpcLevel(
  2298. &g_LogTimerSpinLock
  2299. );
  2300. }
  2301. /***************************************************************************++
  2302. Routine Description:
  2303. UlBufferTimerDpcRoutine :
  2304. Arguments:
  2305. Ignored
  2306. --***************************************************************************/
  2307. VOID
  2308. UlBufferTimerDpcRoutine(
  2309. PKDPC Dpc,
  2310. PVOID DeferredContext,
  2311. PVOID SystemArgument1,
  2312. PVOID SystemArgument2
  2313. )
  2314. {
  2315. PUL_WORK_ITEM pWorkItem;
  2316. UlAcquireSpinLockAtDpcLevel( &g_BufferTimerSpinLock );
  2317. if( g_BufferTimerInitialized )
  2318. {
  2319. //
  2320. // It's not possible to acquire the resource which protects
  2321. // the log list at DISPATCH_LEVEL therefore we will queue a
  2322. // work item for this.
  2323. //
  2324. pWorkItem = (PUL_WORK_ITEM) UL_ALLOCATE_POOL(
  2325. NonPagedPool,
  2326. sizeof(*pWorkItem),
  2327. UL_WORK_ITEM_POOL_TAG
  2328. );
  2329. if ( pWorkItem )
  2330. {
  2331. UL_QUEUE_WORK_ITEM(
  2332. pWorkItem,
  2333. &UlBufferTimerHandler
  2334. );
  2335. }
  2336. else
  2337. {
  2338. UlTrace(LOGGING,("Ul!UlBufferTimerDpcRoutine: Not enough memory.\n"));
  2339. }
  2340. }
  2341. UlReleaseSpinLockFromDpcLevel( &g_BufferTimerSpinLock );
  2342. }
  2343. /***************************************************************************++
  2344. Routine Description:
  2345. UlLogBufferTimerHandler :
  2346. Work item for the threadpool that goes thru the log list and
  2347. flush the log's file buffers.
  2348. Arguments:
  2349. PUL_WORK_ITEM - Ignored but cleaned up at the end
  2350. --***************************************************************************/
  2351. VOID
  2352. UlBufferTimerHandler(
  2353. IN PUL_WORK_ITEM pWorkItem
  2354. )
  2355. {
  2356. NTSTATUS Status;
  2357. PLIST_ENTRY pLink;
  2358. PUL_LOG_FILE_ENTRY pEntry;
  2359. PAGED_CODE();
  2360. UlTrace(LOGGING,("Ul!UlBufferTimerHandler: scanning the log entries ...\n"));
  2361. UlAcquireResourceShared(&g_pUlNonpagedData->LogListResource, TRUE);
  2362. for (pLink = g_LogListHead.Flink;
  2363. pLink != &g_LogListHead;
  2364. pLink = pLink->Flink
  2365. )
  2366. {
  2367. pEntry = CONTAINING_RECORD(
  2368. pLink,
  2369. UL_LOG_FILE_ENTRY,
  2370. LogFileListEntry
  2371. );
  2372. UlAcquireResourceExclusive(&pEntry->EntryResource, TRUE);
  2373. Status = UlpFlushLogFile( pEntry );
  2374. // TODO: Handle STATUS_DISK_FULL
  2375. UlReleaseResource(&pEntry->EntryResource);
  2376. }
  2377. UlReleaseResource(&g_pUlNonpagedData->LogListResource);
  2378. //
  2379. // Free the memory allocated (ByDpcRoutine below) to
  2380. // this work item.
  2381. //
  2382. UL_FREE_POOL( pWorkItem, UL_WORK_ITEM_POOL_TAG );
  2383. }
  2384. /***************************************************************************++
  2385. Routine Description:
  2386. UlpGrowLogEntry:
  2387. All it does is to grow the log file entry in the log list. So that
  2388. recycle function later on can successfully updates the new log file
  2389. name. This function get called only when Dir Name is changed. Because
  2390. only that case causes us to enlarge the already allocated name buffer.
  2391. Name changes originated from timer based or size based recycling
  2392. does not require us to enlarge as we initially allocates more than enough
  2393. buffer for those changes.
  2394. You should HAVE the log list resource exclusively before calling
  2395. this function.
  2396. Arguments:
  2397. too many
  2398. Return Value:
  2399. NTSTATUS - Completion status.
  2400. --***************************************************************************/
  2401. NTSTATUS
  2402. UlpGrowLogEntry(
  2403. IN PUL_CONFIG_GROUP_OBJECT pConfigGroup,
  2404. IN PUL_LOG_FILE_ENTRY pOldEntry
  2405. )
  2406. {
  2407. NTSTATUS Status;
  2408. USHORT FullPathFileNameLength;
  2409. UNICODE_STRING JunkFileName;
  2410. PUL_LOG_FILE_ENTRY pEntry;
  2411. UNICODE_STRING DosDevice;
  2412. //
  2413. // Sanity check.
  2414. //
  2415. PAGED_CODE();
  2416. ASSERT(pConfigGroup != NULL);
  2417. ASSERT(pOldEntry != NULL);
  2418. Status = STATUS_SUCCESS;
  2419. pEntry = NULL;
  2420. // TODO: Get rid of this function by making sure that log entry never grows.
  2421. // TODO: It means Allocating the Log Dir Name separately not inline.
  2422. UlTrace( LOGGING, ("Ul!UlpGrowLogEntry: old_entry %p\n", pOldEntry ));
  2423. RtlInitUnicodeString( &JunkFileName, L"\\none.log" );
  2424. FullPathFileNameLength= UL_MAX_PATH_PREFIX_LENGTH +
  2425. pConfigGroup->LoggingConfig.LogFileDir.Length +
  2426. (UL_MAX_FILE_NAME_SUFFIX_LENGTH+1) * sizeof(WCHAR);
  2427. pEntry = UL_ALLOCATE_STRUCT_WITH_SPACE(
  2428. NonPagedPool,
  2429. UL_LOG_FILE_ENTRY,
  2430. FullPathFileNameLength,
  2431. UL_LOG_FILE_ENTRY_POOL_TAG
  2432. );
  2433. if ( pEntry == NULL )
  2434. {
  2435. Status = STATUS_INSUFFICIENT_RESOURCES;
  2436. goto end;
  2437. }
  2438. RtlZeroMemory( pEntry, sizeof(*pEntry) );
  2439. pEntry->Signature = UL_LOG_FILE_ENTRY_POOL_TAG;
  2440. *pEntry = *pOldEntry;
  2441. //
  2442. // Construct a default dir_name. Don't worry about the
  2443. // L"none.log", it will be overwritten by the recycler
  2444. // later on, as long as there's MAX_LOG_FILE_NAME_SIZE
  2445. // space for the time/type dependent part of the file
  2446. // name ( aka short file name ), it's all right.
  2447. //
  2448. pEntry->FileName.Length = 0;
  2449. pEntry->FileName.MaximumLength = FullPathFileNameLength;
  2450. pEntry->FileName.Buffer = (PWSTR) ( pEntry + 1 );
  2451. if (pConfigGroup->LoggingConfig.LogFileDir.Buffer[0] == L'\\')
  2452. {
  2453. if (pConfigGroup->LoggingConfig.LogFileDir.Buffer[1] == L'\\')
  2454. {
  2455. // UNC share: "\\alitudev\temp"
  2456. RtlInitUnicodeString( &DosDevice, UL_UNC_PATH_PREFIX );
  2457. }
  2458. else
  2459. {
  2460. // Local Directory name is missing the device i.e "\temp"
  2461. // It should be fully qualified name.
  2462. Status = STATUS_NOT_SUPPORTED;
  2463. goto end;
  2464. }
  2465. // Skip second backslash for the UNC path
  2466. RtlCopyUnicodeString( &pEntry->FileName, &DosDevice );
  2467. RtlCopyMemory(
  2468. &(pEntry->FileName.Buffer[pEntry->FileName.Length/sizeof(WCHAR)]),
  2469. &(pConfigGroup->LoggingConfig.LogFileDir.Buffer[1]),
  2470. pConfigGroup->LoggingConfig.LogFileDir.Length - sizeof(WCHAR)
  2471. );
  2472. pEntry->FileName.Length +=
  2473. (pConfigGroup->LoggingConfig.LogFileDir.Length - sizeof(WCHAR));
  2474. }
  2475. else
  2476. {
  2477. RtlInitUnicodeString( &DosDevice, UL_LOCAL_PATH_PREFIX );
  2478. RtlCopyUnicodeString( &pEntry->FileName, &DosDevice );
  2479. RtlAppendUnicodeStringToString(
  2480. &pEntry->FileName,
  2481. &(pConfigGroup->LoggingConfig.LogFileDir)
  2482. );
  2483. }
  2484. pEntry->pShortName =
  2485. (PWSTR) &pEntry->FileName.Buffer[pEntry->FileName.Length/sizeof(WCHAR)];
  2486. Status = RtlAppendUnicodeStringToString(
  2487. &pEntry->FileName,
  2488. &JunkFileName
  2489. );
  2490. if (NT_SUCCESS(Status) == FALSE)
  2491. goto end;
  2492. //
  2493. // Do the replacement here
  2494. //
  2495. RemoveEntryList(&pOldEntry->LogFileListEntry);
  2496. pOldEntry->LogFileListEntry.Flink =
  2497. pOldEntry->LogFileListEntry.Blink = NULL;
  2498. //
  2499. // Restore/Carry the buffer from the old entry
  2500. //
  2501. pEntry->Flags.LogTitleWritten = pOldEntry->Flags.LogTitleWritten;
  2502. pEntry->LogBuffer = pOldEntry->LogBuffer;
  2503. KeInitializeEvent(&pEntry->CloseEvent, NotificationEvent, FALSE);
  2504. // Create the new entries eresource
  2505. Status = UlInitializeResource(&pEntry->EntryResource,"EntryResource",0,
  2506. UL_LOG_FILE_ENTRY_POOL_TAG);
  2507. ASSERT(NT_SUCCESS(Status));
  2508. // Delete old entry eresource
  2509. Status = UlDeleteResource( &pOldEntry->EntryResource );
  2510. ASSERT(NT_SUCCESS(Status));
  2511. UL_FREE_POOL_WITH_SIG(pOldEntry,UL_LOG_FILE_ENTRY_POOL_TAG);
  2512. InsertHeadList(&g_LogListHead, &pEntry->LogFileListEntry);
  2513. pConfigGroup->pLogFileEntry = pEntry;
  2514. //
  2515. // Lets return happily
  2516. //
  2517. end:
  2518. if (!NT_SUCCESS(Status))
  2519. {
  2520. UlTrace(LOGGING,
  2521. ("Ul!UlpGrowLogEntry: old_entry %p, file %S, failure %08lx\n",
  2522. pOldEntry,
  2523. pOldEntry->FileName.Buffer,
  2524. Status
  2525. ));
  2526. if (pEntry)
  2527. {
  2528. NTSTATUS TempStatus;
  2529. // Delete the entry eresource
  2530. TempStatus = UlDeleteResource( &pEntry->EntryResource );
  2531. ASSERT(NT_SUCCESS(TempStatus));
  2532. UL_FREE_POOL_WITH_SIG(pEntry,UL_LOG_FILE_ENTRY_POOL_TAG);
  2533. }
  2534. }
  2535. return Status;
  2536. }
  2537. /***************************************************************************++
  2538. Routine Description:
  2539. UlReconfigureLogEntry :
  2540. This function implements the logging reconfiguration per attribute.
  2541. Everytime config changes happens we try to update the existing logging
  2542. parameters here.
  2543. Arguments:
  2544. pConfig - corresponding cgroup object
  2545. Return Value:
  2546. NTSTATUS - Completion status.
  2547. --***************************************************************************/
  2548. NTSTATUS
  2549. UlReConfigureLogEntry(
  2550. IN PUL_CONFIG_GROUP_OBJECT pConfigGroup,
  2551. IN PHTTP_CONFIG_GROUP_LOGGING pCfgOld,
  2552. IN PHTTP_CONFIG_GROUP_LOGGING pCfgNew
  2553. )
  2554. {
  2555. NTSTATUS Status ;
  2556. BOOLEAN HaveToReCycle;
  2557. //
  2558. // Sanity check first
  2559. //
  2560. PAGED_CODE();
  2561. Status = STATUS_SUCCESS;
  2562. HaveToReCycle = FALSE;
  2563. UlTrace( LOGGING, ("Ul!UlReConfigureLogEntry: entry %p\n",
  2564. pConfigGroup->pLogFileEntry ));
  2565. if ( pCfgOld->LoggingEnabled==FALSE && pCfgNew->LoggingEnabled==FALSE )
  2566. {
  2567. //
  2568. // Do Nothing. Not Even update the fields
  2569. // as soon as we get enable request,
  2570. // field update will take place anyway.
  2571. //
  2572. return Status;
  2573. }
  2574. if ( pCfgOld->LoggingEnabled==TRUE && pCfgNew->LoggingEnabled==FALSE )
  2575. {
  2576. //
  2577. // Stop logging but keep the entry in the
  2578. // list. CODEWORK do we have to keep this info
  2579. // in the entry itself and when we recycle the
  2580. // logs based on timer, we do not recycle the
  2581. // ones with logging disabled ??
  2582. //
  2583. pCfgOld->LoggingEnabled = FALSE;
  2584. return Status;
  2585. }
  2586. else
  2587. {
  2588. pCfgOld->LoggingEnabled = TRUE;
  2589. }
  2590. if ( pCfgNew->LogFormat >= HttpLoggingTypeMaximum ||
  2591. pCfgNew->LogPeriod >= HttpLoggingPeriodMaximum )
  2592. {
  2593. return STATUS_INVALID_PARAMETER;
  2594. }
  2595. //
  2596. // An important check to ensure that no infinite loop occurs because of
  2597. // ridiculusly small truncatesizes. Means smaller than maximum
  2598. // allowed log record line (10*1024)
  2599. //
  2600. if ( pCfgNew->LogPeriod == HttpLoggingPeriodMaxSize &&
  2601. pCfgNew->LogFileTruncateSize != HTTP_LIMIT_INFINITE &&
  2602. pCfgNew->LogFileTruncateSize < UL_MIN_ALLOWED_TRUNCATESIZE
  2603. )
  2604. {
  2605. UlTrace( LOGGING,
  2606. ("Ul!UlReConfigureLogEntry: Truncate size too small %d !\n",
  2607. pCfgNew->LogFileTruncateSize
  2608. ));
  2609. return STATUS_INVALID_PARAMETER;
  2610. }
  2611. //
  2612. // No matter what ReConfiguration should acquire the LogListResource
  2613. // exclusively.
  2614. //
  2615. UlAcquireResourceExclusive(&g_pUlNonpagedData->LogListResource, TRUE);
  2616. //
  2617. // Proceed down to see if anything else changed
  2618. // as well ...
  2619. //
  2620. if ( RtlCompareUnicodeString(
  2621. &pCfgNew->LogFileDir, &pCfgOld->LogFileDir, TRUE ) != 0
  2622. )
  2623. {
  2624. //
  2625. // Always force ReGrow otherwise dir name won't be updated
  2626. // Update the cgroup to hold the new log_dir name
  2627. //
  2628. if (pCfgOld->LogFileDir.Buffer != NULL)
  2629. {
  2630. UL_FREE_POOL(
  2631. pCfgOld->LogFileDir.Buffer,
  2632. UL_CG_LOGDIR_POOL_TAG
  2633. );
  2634. pCfgOld->LogFileDir.Buffer = NULL;
  2635. }
  2636. pCfgOld->LogFileDir.Buffer =
  2637. (PWSTR) UL_ALLOCATE_ARRAY(
  2638. PagedPool,
  2639. UCHAR,
  2640. pCfgNew->LogFileDir.MaximumLength,
  2641. UL_CG_LOGDIR_POOL_TAG
  2642. );
  2643. if (pCfgOld->LogFileDir.Buffer == NULL)
  2644. {
  2645. pCfgOld->LogFileDir.Length = 0;
  2646. pCfgOld->LogFileDir.MaximumLength = 0;
  2647. Status = STATUS_NO_MEMORY;
  2648. goto end;
  2649. }
  2650. RtlCopyMemory(
  2651. pCfgOld->LogFileDir.Buffer,
  2652. pCfgNew->LogFileDir.Buffer,
  2653. pCfgNew->LogFileDir.MaximumLength
  2654. );
  2655. pCfgOld->LogFileDir.Length = pCfgNew->LogFileDir.Length;
  2656. pCfgOld->LogFileDir.MaximumLength = pCfgNew->LogFileDir.MaximumLength;
  2657. UlpGrowLogEntry( pConfigGroup, pConfigGroup->pLogFileEntry );
  2658. // Need to find the proper sequence number after scanning the new
  2659. // log directory.
  2660. SET_SEQUNCE_NUMBER_STALE(pConfigGroup->pLogFileEntry);
  2661. HaveToReCycle = TRUE;
  2662. }
  2663. if ( pCfgNew->LogFormat != pCfgOld->LogFormat )
  2664. {
  2665. Status = UlpUpdateFormat(
  2666. pConfigGroup->pLogFileEntry,
  2667. pCfgOld,
  2668. pCfgNew
  2669. );
  2670. goto end;
  2671. }
  2672. if ( pCfgNew->LogPeriod != pCfgOld->LogPeriod )
  2673. {
  2674. Status = UlpUpdatePeriod(
  2675. pConfigGroup->pLogFileEntry,
  2676. pCfgOld,
  2677. pCfgNew
  2678. );
  2679. goto end;
  2680. }
  2681. //
  2682. // Both LogFormat and LogPeriod always trigger the recycle and they also
  2683. // handle changes to the LogFileTruncateSize and LogExtFileFlags before
  2684. // triggering the recycle. Therefore we can bail out safely.
  2685. //
  2686. if ( pCfgNew->LogFileTruncateSize != pCfgOld->LogFileTruncateSize )
  2687. {
  2688. Status = UlpUpdateLogTruncateSize(
  2689. pConfigGroup->pLogFileEntry,
  2690. pCfgOld,
  2691. pCfgNew,
  2692. &HaveToReCycle
  2693. );
  2694. }
  2695. if ( pCfgNew->LogExtFileFlags != pCfgOld->LogExtFileFlags )
  2696. {
  2697. //
  2698. // Just a change in the flags should not cause us to recyle.
  2699. // Unless something else is also changed. If that's the case
  2700. // then it's either handled above us, or will be handled down
  2701. //
  2702. Status = UlpUpdateLogFlags(
  2703. pConfigGroup->pLogFileEntry,
  2704. pCfgOld,
  2705. pCfgNew
  2706. );
  2707. }
  2708. if ( HaveToReCycle )
  2709. {
  2710. //
  2711. // If we are here that means directory name has been changed
  2712. // and nobody has recycled the log file yet. So time to go
  2713. //
  2714. Status = UlpRecycleLogFile(pConfigGroup->pLogFileEntry);
  2715. }
  2716. end:
  2717. if (NT_SUCCESS(Status) == FALSE)
  2718. {
  2719. UlTrace( LOGGING,
  2720. ("Ul!UlReConfigureLogEntry: entry %p, failure %08lx\n",
  2721. pConfigGroup->pLogFileEntry,
  2722. Status
  2723. ));
  2724. }
  2725. UlReleaseResource(&g_pUlNonpagedData->LogListResource);
  2726. return Status;
  2727. } // UlReConfigureLogEntry
  2728. /***************************************************************************++
  2729. Routine Description:
  2730. UlpUpdateFormat :
  2731. Arguments:
  2732. pConfig - corresponding cgroup object
  2733. --***************************************************************************/
  2734. NTSTATUS
  2735. UlpUpdateFormat(
  2736. OUT PUL_LOG_FILE_ENTRY pEntry,
  2737. IN PHTTP_CONFIG_GROUP_LOGGING pCfgOld,
  2738. IN PHTTP_CONFIG_GROUP_LOGGING pCfgNew
  2739. )
  2740. {
  2741. NTSTATUS Status ;
  2742. TIME_FIELDS CurrentTimeFields;
  2743. LARGE_INTEGER CurrentTimeStamp;
  2744. PAGED_CODE();
  2745. Status = STATUS_SUCCESS;
  2746. if ( pEntry == NULL )
  2747. {
  2748. return STATUS_INVALID_PARAMETER;
  2749. }
  2750. ASSERT(IS_VALID_LOG_FILE_ENTRY(pEntry));
  2751. ASSERT(pCfgOld->LogFormat==pEntry->Format);
  2752. pCfgOld->LogFormat = pCfgNew->LogFormat;
  2753. pEntry->Format = pCfgNew->LogFormat;
  2754. pCfgOld->LogPeriod = pCfgNew->LogPeriod;
  2755. pEntry->Period = (HTTP_LOGGING_PERIOD) pCfgNew->LogPeriod;
  2756. pEntry->TruncateSize = pCfgNew->LogFileTruncateSize;
  2757. pCfgOld->LogFileTruncateSize= pCfgNew->LogFileTruncateSize;
  2758. pEntry->LogExtFileFlags = pCfgNew->LogExtFileFlags;
  2759. pCfgOld->LogExtFileFlags = pCfgNew->LogExtFileFlags;
  2760. KeQuerySystemTime( &CurrentTimeStamp );
  2761. RtlTimeToTimeFields( &CurrentTimeStamp, &CurrentTimeFields );
  2762. if ( pEntry->Period != HttpLoggingPeriodMaxSize )
  2763. {
  2764. Status = UlpCalculateTimeToExpire(
  2765. &CurrentTimeFields,
  2766. pEntry->Period,
  2767. &pEntry->TimeToExpire
  2768. );
  2769. ASSERT(NT_SUCCESS(Status)==TRUE);
  2770. }
  2771. ASSERT(NT_SUCCESS(Status)==TRUE);
  2772. SET_SEQUNCE_NUMBER_STALE(pEntry);
  2773. Status = UlpRecycleLogFile( pEntry );
  2774. return Status;
  2775. }
  2776. /***************************************************************************++
  2777. Routine Description:
  2778. UlpUpdatePeriod :
  2779. Arguments:
  2780. pConfig - corresponding cgroup object
  2781. Return Value:
  2782. NTSTATUS - Completion status.
  2783. --***************************************************************************/
  2784. NTSTATUS
  2785. UlpUpdatePeriod(
  2786. OUT PUL_LOG_FILE_ENTRY pEntry,
  2787. IN PHTTP_CONFIG_GROUP_LOGGING pCfgOld,
  2788. IN PHTTP_CONFIG_GROUP_LOGGING pCfgNew
  2789. )
  2790. {
  2791. NTSTATUS Status ;
  2792. TIME_FIELDS CurrentTimeFields;
  2793. LARGE_INTEGER CurrentTimeStamp;
  2794. PAGED_CODE();
  2795. Status = STATUS_SUCCESS;
  2796. if ( pEntry == NULL )
  2797. {
  2798. return STATUS_INVALID_PARAMETER;
  2799. }
  2800. ASSERT(IS_VALID_LOG_FILE_ENTRY(pEntry));
  2801. ASSERT(pCfgOld->LogPeriod==(ULONG)pEntry->Period);
  2802. pCfgOld->LogPeriod = pCfgNew->LogPeriod;
  2803. pEntry->Period = (HTTP_LOGGING_PERIOD) pCfgNew->LogPeriod;
  2804. pEntry->TruncateSize = pCfgNew->LogFileTruncateSize;
  2805. pCfgOld->LogFileTruncateSize= pCfgNew->LogFileTruncateSize;
  2806. pEntry->LogExtFileFlags = pCfgNew->LogExtFileFlags;
  2807. pCfgOld->LogExtFileFlags = pCfgNew->LogExtFileFlags;
  2808. KeQuerySystemTime( &CurrentTimeStamp );
  2809. RtlTimeToTimeFields( &CurrentTimeStamp, &CurrentTimeFields );
  2810. if ( pEntry->Period != HttpLoggingPeriodMaxSize )
  2811. {
  2812. Status = UlpCalculateTimeToExpire(
  2813. &CurrentTimeFields,
  2814. pEntry->Period,
  2815. &pEntry->TimeToExpire
  2816. );
  2817. ASSERT(NT_SUCCESS(Status)==TRUE);
  2818. }
  2819. SET_SEQUNCE_NUMBER_STALE(pEntry);
  2820. Status = UlpRecycleLogFile( pEntry );
  2821. return Status;
  2822. }
  2823. /***************************************************************************++
  2824. Routine Description:
  2825. UlpUpdateLogTruncateSize :
  2826. Arguments:
  2827. pConfig - corresponding cgroup object
  2828. Return Value:
  2829. NTSTATUS - Completion status.
  2830. --***************************************************************************/
  2831. NTSTATUS
  2832. UlpUpdateLogTruncateSize(
  2833. OUT PUL_LOG_FILE_ENTRY pEntry,
  2834. IN PHTTP_CONFIG_GROUP_LOGGING pCfgOld,
  2835. IN PHTTP_CONFIG_GROUP_LOGGING pCfgNew,
  2836. OUT BOOLEAN * pHaveToReCycle
  2837. )
  2838. {
  2839. NTSTATUS Status ;
  2840. //
  2841. // Sanity check first
  2842. //
  2843. PAGED_CODE();
  2844. Status = STATUS_SUCCESS;
  2845. //
  2846. // For MAX_SIZE period type we should check if
  2847. // limited => unlimited:
  2848. // we can still use the last log file
  2849. // unlimited => limited:
  2850. // we should open a new one if old size is larger than
  2851. // the new limitation
  2852. //
  2853. if ( pEntry == NULL )
  2854. {
  2855. return STATUS_INVALID_PARAMETER;
  2856. }
  2857. ASSERT(IS_VALID_LOG_FILE_ENTRY(pEntry));
  2858. ASSERT(pCfgOld->LogFileTruncateSize==pEntry->TruncateSize);
  2859. if ( pCfgOld->LogPeriod == HttpLoggingPeriodMaxSize )
  2860. {
  2861. if ( pCfgOld->LogFileTruncateSize == HTTP_LIMIT_INFINITE )
  2862. {
  2863. //
  2864. // Unlimited to Limited
  2865. //
  2866. if ( pEntry->TotalWritten.QuadPart >
  2867. (ULONGLONG)pCfgNew->LogFileTruncateSize )
  2868. {
  2869. // In case flags get changed lets take it too
  2870. pEntry->LogExtFileFlags = pCfgNew->LogExtFileFlags;
  2871. pCfgOld->LogExtFileFlags = pCfgNew->LogExtFileFlags;
  2872. UlpRecycleLogFile( pEntry );
  2873. *pHaveToReCycle = FALSE;
  2874. }
  2875. }
  2876. else
  2877. {
  2878. //
  2879. // Limited to Unlimited
  2880. // Nothing special to do
  2881. //
  2882. }
  2883. }
  2884. pEntry->TruncateSize = pCfgNew->LogFileTruncateSize;
  2885. pCfgOld->LogFileTruncateSize = pCfgNew->LogFileTruncateSize;
  2886. return Status;
  2887. }
  2888. /***************************************************************************++
  2889. Routine Description:
  2890. UlpUpdateLogFlags :
  2891. REQUIRES caller to have loglist resource exclusively.
  2892. Arguments:
  2893. pEntry - corresponding logfile entry
  2894. old & new configuration
  2895. --***************************************************************************/
  2896. NTSTATUS
  2897. UlpUpdateLogFlags(
  2898. OUT PUL_LOG_FILE_ENTRY pEntry,
  2899. IN PHTTP_CONFIG_GROUP_LOGGING pCfgOld,
  2900. IN PHTTP_CONFIG_GROUP_LOGGING pCfgNew
  2901. )
  2902. {
  2903. NTSTATUS Status ;
  2904. TIME_FIELDS CurrentTimeFields;
  2905. LARGE_INTEGER CurrentTimeStamp;
  2906. //
  2907. // No need to do anything else, we will
  2908. // just display a new title with the
  2909. // new fields.
  2910. //
  2911. PAGED_CODE();
  2912. Status = STATUS_SUCCESS;
  2913. if (pEntry == NULL)
  2914. {
  2915. return STATUS_INVALID_PARAMETER;
  2916. }
  2917. ASSERT(IS_VALID_LOG_FILE_ENTRY(pEntry));
  2918. ASSERT(pCfgOld->LogExtFileFlags==pEntry->LogExtFileFlags);
  2919. pCfgOld->LogExtFileFlags = pCfgNew->LogExtFileFlags;
  2920. pEntry->LogExtFileFlags = pCfgNew->LogExtFileFlags;
  2921. if (pEntry->Format == HttpLoggingTypeW3C)
  2922. {
  2923. pEntry->Flags.LogTitleWritten = 0;
  2924. }
  2925. return Status;
  2926. }
  2927. /***************************************************************************++
  2928. Routine Description:
  2929. UlpConstructLogFileEntry :
  2930. Finds out the correct file name for the newly created log file
  2931. from our - current time dependent- time-to-name converters. Also
  2932. allocates the necessary file entry from paged pool. This entry get
  2933. removed from the list when the corresponding config group object
  2934. has been destroyed. At that time RemoveLogFile entry called and
  2935. it frees this memory.
  2936. Arguments:
  2937. pConfig - corresponding cgroup object
  2938. ppEntry - will point to newly created entry
  2939. pDirectoryName - the directory name to store newcoming log file
  2940. Return Value:
  2941. NTSTATUS - Completion status.
  2942. --***************************************************************************/
  2943. NTSTATUS
  2944. UlpConstructLogFileEntry(
  2945. IN PHTTP_CONFIG_GROUP_LOGGING pConfig,
  2946. OUT PUL_LOG_FILE_ENTRY * ppEntry,
  2947. OUT PUNICODE_STRING pDirectoryName,
  2948. IN PTIME_FIELDS pCurrentTimeFields
  2949. )
  2950. {
  2951. NTSTATUS Status,TmpStatus;
  2952. USHORT FullPathFileNameLength;
  2953. ULONG SequenceNumber;
  2954. PUL_LOG_FILE_ENTRY pEntry;
  2955. WCHAR _FileName[UL_MAX_FILE_NAME_SUFFIX_LENGTH+1];
  2956. UNICODE_STRING FileName =
  2957. { 0, (UL_MAX_FILE_NAME_SUFFIX_LENGTH+1)*sizeof(WCHAR), _FileName };
  2958. //
  2959. // Sanity check.
  2960. //
  2961. PAGED_CODE();
  2962. ASSERT(pConfig != NULL);
  2963. ASSERT(ppEntry != NULL);
  2964. Status = STATUS_SUCCESS;
  2965. SequenceNumber = 1;
  2966. pEntry = NULL;
  2967. FullPathFileNameLength = pDirectoryName->Length;
  2968. UlpConstructFileName(
  2969. (HTTP_LOGGING_PERIOD) pConfig->LogPeriod,
  2970. UL_GET_LOG_FILE_NAME_PREFIX(pConfig->LogFormat),
  2971. &FileName,
  2972. pCurrentTimeFields,
  2973. &SequenceNumber
  2974. );
  2975. FullPathFileNameLength += FileName.Length;
  2976. //
  2977. // Allocate a memory for our new logfile entry in the list.
  2978. // To avoid the frequent reallocs for the log entry - E.g.
  2979. // we receive a timer update and filename changes according to
  2980. // new time - , We will try to allocate a fixed amount here
  2981. // for all the possible file_names ( this doesn't include
  2982. // the log_dir changes may happen from WAS through cgroup
  2983. // in that case we will realloc a new entry ). It has to
  2984. // be nonpaged because it holds a eresource.
  2985. //
  2986. pEntry = UL_ALLOCATE_STRUCT_WITH_SPACE(
  2987. NonPagedPool,
  2988. UL_LOG_FILE_ENTRY,
  2989. (UL_MAX_FILE_NAME_SUFFIX_LENGTH + 1) * sizeof(WCHAR) +
  2990. pDirectoryName->Length,
  2991. UL_LOG_FILE_ENTRY_POOL_TAG
  2992. );
  2993. if ( pEntry == NULL )
  2994. {
  2995. Status = STATUS_NO_MEMORY;
  2996. goto end;
  2997. }
  2998. RtlZeroMemory( pEntry, sizeof(*pEntry) );
  2999. pEntry->Signature = UL_LOG_FILE_ENTRY_POOL_TAG;
  3000. //
  3001. // Concat the directory & file name properly.
  3002. //
  3003. pEntry->FileName.Length = FullPathFileNameLength;
  3004. pEntry->FileName.MaximumLength= (UL_MAX_FILE_NAME_SUFFIX_LENGTH+1) * sizeof(WCHAR) +
  3005. pDirectoryName->Length;
  3006. pEntry->FileName.Buffer = (PWSTR) ( pEntry + 1 );
  3007. RtlCopyUnicodeString( &(pEntry->FileName), pDirectoryName );
  3008. Status = RtlAppendUnicodeStringToString( &(pEntry->FileName), &FileName );
  3009. ASSERT(NT_SUCCESS(Status));
  3010. pEntry->FileName.Buffer[FullPathFileNameLength/sizeof(WCHAR)] = UNICODE_NULL;
  3011. pEntry->pShortName = (PWSTR)
  3012. &(pEntry->FileName.Buffer[pDirectoryName->Length/sizeof(WCHAR)]);
  3013. //
  3014. // Create a log entry buffer of system dependent size
  3015. // typically 64K
  3016. //
  3017. Status = UlInitializeResource(&pEntry->EntryResource,"EntryResource",0,
  3018. UL_LOG_FILE_ENTRY_POOL_TAG);
  3019. ASSERT(NT_SUCCESS(Status));
  3020. //
  3021. // Initialize the file handle
  3022. //
  3023. pEntry->hFile = NULL;
  3024. KeInitializeEvent(&pEntry->CloseEvent, NotificationEvent, FALSE);
  3025. //
  3026. // Set the logging information from config group
  3027. // easier for other routines to use this values
  3028. // w/o reaching to the config-group
  3029. //
  3030. pEntry->Format = pConfig->LogFormat;
  3031. pEntry->Period = (HTTP_LOGGING_PERIOD) pConfig->LogPeriod;
  3032. pEntry->TruncateSize = pConfig->LogFileTruncateSize;
  3033. pEntry->LogExtFileFlags = pConfig->LogExtFileFlags;
  3034. //
  3035. // Time to initialize our Log Cycling parameter
  3036. //
  3037. pEntry->TimeToExpire = 0;
  3038. pEntry->SequenceNumber = SequenceNumber;
  3039. pEntry->TotalWritten.QuadPart = (ULONGLONG)0;
  3040. pEntry->Flags.Value = 0;
  3041. if (pEntry->Format == HttpLoggingTypeW3C)
  3042. {
  3043. pEntry->Flags.LogTitleWritten = 0;
  3044. }
  3045. else
  3046. {
  3047. pEntry->Flags.LogTitleWritten = 1;
  3048. }
  3049. pEntry->LogBuffer = NULL;
  3050. Status = UlpCalculateTimeToExpire(
  3051. pCurrentTimeFields,
  3052. pEntry->Period,
  3053. &pEntry->TimeToExpire
  3054. );
  3055. if ( !NT_SUCCESS(Status) )
  3056. {
  3057. goto end;
  3058. }
  3059. //
  3060. // Lets happily return our entry
  3061. //
  3062. *ppEntry = pEntry;
  3063. end:
  3064. if ( !NT_SUCCESS(Status) )
  3065. {
  3066. if ( pEntry )
  3067. {
  3068. NTSTATUS TempStatus;
  3069. if ( pEntry->LogBuffer )
  3070. {
  3071. UlPplFreeLogBuffer( pEntry->LogBuffer );
  3072. }
  3073. // Delete the entry eresource
  3074. TempStatus = UlDeleteResource( &pEntry->EntryResource );
  3075. ASSERT(NT_SUCCESS(TempStatus));
  3076. UL_FREE_POOL_WITH_SIG( pEntry, UL_LOG_FILE_ENTRY_POOL_TAG );
  3077. }
  3078. }
  3079. return Status;
  3080. }
  3081. /***************************************************************************++
  3082. Routine Description:
  3083. UlpConstructFileName:
  3084. A bunch of current_time TO file_name conversions comes here ...
  3085. Arguments:
  3086. period - period type of the log
  3087. prefix - any prefix to be added to the file name
  3088. filename - result file name
  3089. fields - time fields
  3090. Return Value:
  3091. VOID - No return value.
  3092. --***************************************************************************/
  3093. VOID
  3094. UlpConstructFileName(
  3095. IN HTTP_LOGGING_PERIOD period,
  3096. IN PCWSTR prefix,
  3097. OUT PUNICODE_STRING filename,
  3098. IN PTIME_FIELDS fields,
  3099. IN OUT PULONG sequenceNu //OPTIONAL
  3100. )
  3101. {
  3102. WCHAR _tmp[UL_MAX_FILE_NAME_SUFFIX_LENGTH+1];
  3103. UNICODE_STRING tmp = { 0, 0, _tmp };
  3104. CSHORT Year;
  3105. LONG WcharsCopied = 0L;
  3106. PAGED_CODE();
  3107. //
  3108. // Retain just last 2 digits of the Year
  3109. //
  3110. tmp.MaximumLength = (UL_MAX_FILE_NAME_SUFFIX_LENGTH+1) * sizeof(WCHAR);
  3111. if (fields)
  3112. {
  3113. Year = fields->Year % 100;
  3114. }
  3115. switch ( period )
  3116. {
  3117. case HttpLoggingPeriodHourly:
  3118. {
  3119. WcharsCopied =
  3120. _snwprintf( _tmp,
  3121. UL_MAX_FILE_NAME_SUFFIX_LENGTH,
  3122. (UTF8_LOGGING_ENABLED() ?
  3123. L"%.5s%02.2d%02d%02d%02d.%s" :
  3124. L"%.3s%02.2d%02d%02d%02d.%s"),
  3125. prefix,
  3126. Year,
  3127. fields->Month,
  3128. fields->Day,
  3129. fields->Hour,
  3130. DEFAULT_LOG_FILE_EXTENSION
  3131. );
  3132. }
  3133. break;
  3134. case HttpLoggingPeriodDaily:
  3135. {
  3136. WcharsCopied =
  3137. _snwprintf( _tmp,
  3138. UL_MAX_FILE_NAME_SUFFIX_LENGTH,
  3139. (UTF8_LOGGING_ENABLED() ?
  3140. L"%.5s%02.2d%02d%02d.%s" :
  3141. L"%.3s%02.2d%02d%02d.%s"),
  3142. prefix,
  3143. Year,
  3144. fields->Month,
  3145. fields->Day,
  3146. DEFAULT_LOG_FILE_EXTENSION
  3147. );
  3148. }
  3149. break;
  3150. case HttpLoggingPeriodWeekly:
  3151. {
  3152. WcharsCopied =
  3153. _snwprintf( _tmp,
  3154. UL_MAX_FILE_NAME_SUFFIX_LENGTH,
  3155. (UTF8_LOGGING_ENABLED() ?
  3156. L"%.5s%02.2d%02d%02d.%s" :
  3157. L"%.3s%02.2d%02d%02d.%s"),
  3158. prefix,
  3159. Year,
  3160. fields->Month,
  3161. UlpWeekOfMonth(fields),
  3162. DEFAULT_LOG_FILE_EXTENSION
  3163. );
  3164. }
  3165. break;
  3166. case HttpLoggingPeriodMonthly:
  3167. {
  3168. WcharsCopied =
  3169. _snwprintf( _tmp,
  3170. UL_MAX_FILE_NAME_SUFFIX_LENGTH,
  3171. (UTF8_LOGGING_ENABLED() ?
  3172. L"%.5s%02.2d%02d.%s" :
  3173. L"%.3s%02.2d%02d.%s"),
  3174. prefix,
  3175. Year,
  3176. fields->Month,
  3177. DEFAULT_LOG_FILE_EXTENSION
  3178. );
  3179. }
  3180. break;
  3181. case HttpLoggingPeriodMaxSize:
  3182. {
  3183. if ( sequenceNu != NULL )
  3184. {
  3185. WcharsCopied =
  3186. _snwprintf( _tmp,
  3187. UL_MAX_FILE_NAME_SUFFIX_LENGTH,
  3188. (UTF8_LOGGING_ENABLED() ?
  3189. L"%.9s%d.%s" :
  3190. L"%.7s%d.%s"),
  3191. prefix,
  3192. (*sequenceNu),
  3193. DEFAULT_LOG_FILE_EXTENSION
  3194. );
  3195. (*sequenceNu) += 1;
  3196. }
  3197. else
  3198. {
  3199. ASSERT(!"Improper sequence number !");
  3200. }
  3201. }
  3202. break;
  3203. default:
  3204. {
  3205. //
  3206. // This should never happen ...
  3207. //
  3208. ASSERT(!"Unknown Log Format !");
  3209. WcharsCopied =
  3210. _snwprintf( _tmp,
  3211. UL_MAX_FILE_NAME_SUFFIX_LENGTH,
  3212. L"%.7s?.%s",
  3213. prefix,
  3214. DEFAULT_LOG_FILE_EXTENSION
  3215. );
  3216. }
  3217. }
  3218. //
  3219. // As long as we allocate an enough space for a possible
  3220. // log filename we should never hit to this assert here.
  3221. //
  3222. ASSERT(WcharsCopied >0 );
  3223. if ( WcharsCopied < 0 )
  3224. {
  3225. //
  3226. // This should never happen but lets cover it
  3227. // anyway.
  3228. //
  3229. WcharsCopied = UL_MAX_FILE_NAME_SUFFIX_LENGTH*sizeof(WCHAR);
  3230. tmp.Buffer[UL_MAX_FILE_NAME_SUFFIX_LENGTH] = UNICODE_NULL;
  3231. }
  3232. tmp.Length = (USHORT) WcharsCopied*sizeof(WCHAR);
  3233. tmp.MaximumLength = (UL_MAX_FILE_NAME_SUFFIX_LENGTH+1)*sizeof(WCHAR);
  3234. RtlCopyUnicodeString( filename, &tmp );
  3235. }
  3236. /***************************************************************************++
  3237. Routine Description:
  3238. UlpRecycleLogFile :
  3239. This function requires to have the loglist resource shared,as well as
  3240. the logfile entry mutex to be acquired.
  3241. We do not want anybody to Create/Remove/ReConfig to the entry while
  3242. we are working on it, therefore shared access to the loglist.
  3243. We do not want anybody to Hit/Flush to the entry, therefore
  3244. entry's mutex should be acquired.
  3245. Or otherwise caller might have the loglist resource exclusively and
  3246. this will automatically ensure the safety as well. As it is not
  3247. possible for anybody else to acquire entry mutex first w/o having
  3248. the loglist resource shared at least, according to the current
  3249. design.
  3250. Sometimes it may be necessary to scan the new directory to figure out
  3251. the correct sequence numbe rand the file name. Especially after dir
  3252. name reconfig and/or the period becomes MaskPeriod.
  3253. Arguments:
  3254. pEntry - Points to the existing entry.
  3255. NeedToReCalculate - shows if we have to recalculate the time-to-expire.
  3256. --***************************************************************************/
  3257. NTSTATUS
  3258. UlpRecycleLogFile(
  3259. IN PUL_LOG_FILE_ENTRY pEntry
  3260. )
  3261. {
  3262. NTSTATUS Status;
  3263. OBJECT_ATTRIBUTES ObjectAttributes;
  3264. IO_STATUS_BLOCK IoStatusBlock;
  3265. TIME_FIELDS CurrentTimeFields;
  3266. LARGE_INTEGER CurrentTimeStamp;
  3267. PWCHAR pSrc;
  3268. PWCHAR pDst;
  3269. USHORT Index;
  3270. USHORT OldFileNameLength;
  3271. WCHAR _FileName[UL_MAX_FILE_NAME_SUFFIX_LENGTH];
  3272. UNICODE_STRING FileName =
  3273. { 0, UL_MAX_FILE_NAME_SUFFIX_LENGTH*sizeof(WCHAR), _FileName };
  3274. //
  3275. // Sanity check.
  3276. //
  3277. PAGED_CODE();
  3278. ASSERT(IS_VALID_LOG_FILE_ENTRY(pEntry));
  3279. Status = STATUS_SUCCESS;
  3280. //
  3281. // We have two criterions for the log file name
  3282. // its LogFormat and its LogPeriod
  3283. //
  3284. ASSERT(pEntry->Format < HttpLoggingTypeMaximum);
  3285. ASSERT(pEntry->Period < HttpLoggingPeriodMaximum);
  3286. ASSERT(pEntry->FileName.Length!=0);
  3287. UlTrace( LOGGING, ("Ul!UlpRecycleLogFile: pEntry %p \n", pEntry ));
  3288. //
  3289. // This value is computed for the GMT time zone.
  3290. //
  3291. KeQuerySystemTime( &CurrentTimeStamp );
  3292. RtlTimeToTimeFields( &CurrentTimeStamp, &CurrentTimeFields );
  3293. // Init total written to zero. It may get updated if we scan
  3294. // the directory down below.
  3295. pEntry->TotalWritten.QuadPart = (ULONGLONG)0;
  3296. // If we need to scan the directory. Sequence number should start
  3297. // from 1 again. Set this before constructing the log file name.
  3298. if (pEntry->Flags.StaleSequenceNumber &&
  3299. pEntry->Period==HttpLoggingPeriodMaxSize)
  3300. {
  3301. // Init otherwise if QueryDirectory doesn't find any it
  3302. // will not update this values
  3303. pEntry->SequenceNumber = 1;
  3304. }
  3305. //
  3306. // Now construct the filename using the lookup table
  3307. // And the current time
  3308. //
  3309. UlpConstructFileName(
  3310. pEntry->Period,
  3311. UL_GET_LOG_FILE_NAME_PREFIX(pEntry->Format),
  3312. &FileName,
  3313. &CurrentTimeFields,
  3314. &pEntry->SequenceNumber
  3315. );
  3316. if ( pEntry->FileName.MaximumLength <= FileName.Length )
  3317. {
  3318. ASSERT(FALSE);
  3319. Status = STATUS_INVALID_PARAMETER;
  3320. goto end;
  3321. }
  3322. //
  3323. // Do the magic and renew the filename. Replace the old file
  3324. // name with the new one.
  3325. //
  3326. ASSERT( pEntry->pShortName != NULL );
  3327. if ( pEntry->pShortName != NULL )
  3328. {
  3329. //
  3330. // Get rid of the old filename before flushing the
  3331. // directories and reconcataneting the new file name
  3332. // to the end again.
  3333. //
  3334. *((PWCHAR)pEntry->pShortName) = UNICODE_NULL;
  3335. pEntry->FileName.Length =
  3336. wcslen( pEntry->FileName.Buffer ) * sizeof(WCHAR);
  3337. //
  3338. // Create/Open the director(ies) first. This might be
  3339. // necessary if we get called after an entry reconfiguration
  3340. // and directory name change.
  3341. //
  3342. Status = UlpCreateSafeDirectory( &pEntry->FileName );
  3343. if (!NT_SUCCESS(Status))
  3344. goto end;
  3345. //
  3346. // Now Restore the short file name pointer back
  3347. //
  3348. pEntry->pShortName = (PWSTR)
  3349. &(pEntry->FileName.Buffer[pEntry->FileName.Length/sizeof(WCHAR)]);
  3350. //
  3351. // Append the new file name ( based on updated current time )
  3352. // to the end.
  3353. //
  3354. Status = RtlAppendUnicodeStringToString( &pEntry->FileName, &FileName );
  3355. if (!NT_SUCCESS(Status))
  3356. goto end;
  3357. }
  3358. //
  3359. // If the sequence is stale because of the nature of the recycle.
  3360. // And if our preiod is size based then rescan the new directory
  3361. // to figure out the proper file to open.
  3362. //
  3363. if (pEntry->Flags.StaleSequenceNumber &&
  3364. pEntry->Period==HttpLoggingPeriodMaxSize)
  3365. {
  3366. // This call may update the filename, the file size and the
  3367. // sequence number if there is an old file in the new dir.
  3368. Status = UlpQueryDirectory(pEntry);
  3369. if (!NT_SUCCESS(Status))
  3370. goto end;
  3371. }
  3372. //
  3373. // Time to close the old file and reopen a new one
  3374. //
  3375. if (pEntry->hFile != NULL)
  3376. {
  3377. //
  3378. // Before closing the old one we need to flush the buffer
  3379. //
  3380. UlpFlushLogFile(pEntry);
  3381. UlpLogCloseHandle(pEntry);
  3382. }
  3383. InitializeObjectAttributes(
  3384. &ObjectAttributes,
  3385. &pEntry->FileName, // Full path name
  3386. OBJ_CASE_INSENSITIVE | // Attributes
  3387. UL_KERNEL_HANDLE,
  3388. NULL, // RootDirectory
  3389. NULL // SecurityDescriptor
  3390. );
  3391. //
  3392. // Make the created file Aysnc by not picking the sync flag.
  3393. //
  3394. Status = ZwCreateFile(
  3395. &pEntry->hFile,
  3396. FILE_GENERIC_WRITE,
  3397. &ObjectAttributes,
  3398. &IoStatusBlock,
  3399. NULL,
  3400. FILE_ATTRIBUTE_NORMAL,
  3401. FILE_SHARE_READ,
  3402. FILE_OPEN_IF,
  3403. FILE_NON_DIRECTORY_FILE, // | FILE_SYNCHRONOUS_IO_NONALERT,
  3404. NULL,
  3405. 0);
  3406. if (NT_SUCCESS(Status) == FALSE)
  3407. goto end;
  3408. //
  3409. // Get the file size, etc from the file.
  3410. //
  3411. Status = ZwQueryInformationFile(
  3412. pEntry->hFile,
  3413. &IoStatusBlock,
  3414. &pEntry->FileInfo,
  3415. sizeof(pEntry->FileInfo),
  3416. FileStandardInformation
  3417. );
  3418. if (NT_SUCCESS(Status) == FALSE)
  3419. goto end;
  3420. //
  3421. // Recalculate the time to expire.
  3422. //
  3423. if (pEntry->Flags.StaleTimeToExpire &&
  3424. pEntry->Period != HttpLoggingPeriodMaxSize)
  3425. {
  3426. UlpCalculateTimeToExpire(
  3427. &CurrentTimeFields,
  3428. pEntry->Period,
  3429. &pEntry->TimeToExpire
  3430. );
  3431. }
  3432. //
  3433. // By setting the flag to zero, we mark that we need to write title with the
  3434. // next incoming request But this only applies to W3C format.Otherwise the flag
  3435. // stays as set all the time, and the LogWriter doesn't attempt to write the
  3436. // title for NCSA and IIS log formats with the next incoming request.
  3437. //
  3438. if (pEntry->Format == HttpLoggingTypeW3C)
  3439. {
  3440. pEntry->Flags.LogTitleWritten = 0;
  3441. }
  3442. else
  3443. {
  3444. pEntry->Flags.LogTitleWritten = 1;
  3445. }
  3446. UlTrace( LOGGING, ("Ul!UlpRecycleLogFile: entry %p, file %S, handle %lx\n",
  3447. pEntry,
  3448. pEntry->FileName.Buffer,
  3449. pEntry->hFile
  3450. ));
  3451. end:
  3452. // Mark fields non-stale again;
  3453. RESET_SEQUNCE_NUMBER_STALE(pEntry);
  3454. RESET_TIME_TO_EXPIRE_STALE(pEntry);
  3455. // TODO: Handle STATUS_DISK_FULL case gracefully.
  3456. if ( Status == STATUS_DISK_FULL )
  3457. {
  3458. UlTrace(LOGGING,("UlpRecycleLogFile: DISK FULL entry %p, failure %08lx\n",
  3459. pEntry,
  3460. Status
  3461. ));
  3462. if (pEntry->hFile != NULL)
  3463. {
  3464. ZwClose( pEntry->hFile );
  3465. pEntry->hFile = NULL;
  3466. }
  3467. // TODO: pEntry->Flags.RecyclePending = 1;
  3468. // TODO: UlpFireDiskFullTimer();
  3469. }
  3470. if ( !NT_SUCCESS(Status) && Status != STATUS_DISK_FULL)
  3471. {
  3472. //
  3473. // If we made it to this point, then the create/open has failed.
  3474. //
  3475. UlTrace( LOGGING,("Ul!UlpRecycleLogFile: entry %p, failure %08lx\n",
  3476. pEntry,
  3477. Status
  3478. ));
  3479. if (pEntry->hFile != NULL)
  3480. {
  3481. ZwClose( pEntry->hFile );
  3482. pEntry->hFile = NULL;
  3483. }
  3484. }
  3485. return Status;
  3486. }
  3487. /***************************************************************************++
  3488. Routine Description:
  3489. IsLogFileOverFlow:
  3490. Arguments:
  3491. --***************************************************************************/
  3492. __inline
  3493. BOOLEAN
  3494. UlpIsLogFileOverFlow(
  3495. IN PUL_LOG_FILE_ENTRY pEntry,
  3496. IN ULONG ReqdBytes
  3497. )
  3498. {
  3499. if (pEntry->Period != HttpLoggingPeriodMaxSize ||
  3500. pEntry->TruncateSize == HTTP_LIMIT_INFINITE)
  3501. {
  3502. return FALSE;
  3503. }
  3504. else
  3505. {
  3506. return((pEntry->TotalWritten.QuadPart + (ULONGLONG)ReqdBytes)
  3507. >=
  3508. (ULONGLONG)pEntry->TruncateSize
  3509. );
  3510. }
  3511. }
  3512. /***************************************************************************++
  3513. Routine Description:
  3514. UlpIncrementBytesWritten:
  3515. Arguments:
  3516. --***************************************************************************/
  3517. __inline
  3518. VOID
  3519. UlpIncrementBytesWritten(
  3520. IN PUL_LOG_FILE_ENTRY pEntry,
  3521. IN ULONG BytesWritten
  3522. )
  3523. {
  3524. UlInterlockedAdd64((PLONGLONG) &(pEntry->TotalWritten.QuadPart),
  3525. (ULONGLONG)BytesWritten);
  3526. };
  3527. /***************************************************************************++
  3528. Routine Description:
  3529. UlpWeekOfMonth : Ordinal Number of the week of the current month
  3530. Stolen from IIS 5.1 code base.
  3531. Example
  3532. July 2000 ... :
  3533. S M T W T F S WeekOfMonth
  3534. 1 1
  3535. 2 3 4 5 6 7 8 2
  3536. 9 10 11 12 13 14 15 3
  3537. 16 17 18 19 20 21 22 4
  3538. 23 24 25 26 27 28 29 5
  3539. 30 31 6
  3540. Finds the ordinal number of the week of current month.
  3541. The numbering of weeks starts from 1 and run through 6 per month (max).
  3542. The week number changes only on sundays.
  3543. The calculation to be use is:
  3544. 1 + (dayOfMonth - 1)/7 + ((dayOfMonth - 1) % 7 > dayOfWeek);
  3545. (a) (b) (c) (d)
  3546. (a) to set the week numbers to begin from week numbered "1"
  3547. (b) used to calculate the rough number of the week on which a given
  3548. day falls based on the date.
  3549. (c) calculates what is the offset from the start of week for a given
  3550. day based on the fact that a week had 7 days.
  3551. (d) is the raw day of week given to us.
  3552. (c) > (d) indicates that the week is rolling forward and hence
  3553. the week count should be offset by 1 more.
  3554. Arguments:
  3555. PTIME_FIELDS - system time fields
  3556. Return Value:
  3557. ULONG - This func magically returns the week of the month
  3558. --***************************************************************************/
  3559. __inline
  3560. ULONG UlpWeekOfMonth(
  3561. IN PTIME_FIELDS fields
  3562. )
  3563. {
  3564. ULONG Tmp;
  3565. Tmp = (fields->Day - 1);
  3566. Tmp = ( 1 + Tmp/7 + (((Tmp % 7) > ((ULONG) fields->Weekday)) ? 1 : 0));
  3567. return Tmp;
  3568. }
  3569. /***************************************************************************++
  3570. Routine Description:
  3571. UlpInitializeGMTOffset :
  3572. Calculates and builds the time difference string.
  3573. Get called during the initialization.
  3574. And every hour after that.
  3575. --***************************************************************************/
  3576. VOID
  3577. UlpGetGMTOffset()
  3578. {
  3579. RTL_TIME_ZONE_INFORMATION Tzi;
  3580. NTSTATUS Status;
  3581. CHAR Sign;
  3582. LONG Bias;
  3583. ULONG Hour;
  3584. ULONG Minute;
  3585. ULONG DT = UL_TIME_ZONE_ID_UNKNOWN;
  3586. LONG BiasN = 0;
  3587. PAGED_CODE();
  3588. //
  3589. // get the timezone data from the system
  3590. //
  3591. Status = NtQuerySystemInformation(
  3592. SystemCurrentTimeZoneInformation,
  3593. (PVOID)&Tzi,
  3594. sizeof(Tzi),
  3595. NULL
  3596. );
  3597. if (!NT_SUCCESS(Status))
  3598. {
  3599. UlTrace(LOGGING,("Ul!UlpGetGMTOffset: failure %08lx\n", Status));
  3600. }
  3601. else
  3602. {
  3603. DT = UlCalcTimeZoneIdAndBias(&Tzi, &BiasN);
  3604. }
  3605. if ( BiasN > 0 )
  3606. {
  3607. //
  3608. // UTC = local time + bias
  3609. //
  3610. Bias = BiasN;
  3611. Sign = '-';
  3612. }
  3613. else
  3614. {
  3615. Bias = -1 * BiasN;
  3616. Sign = '+';
  3617. }
  3618. Minute = Bias % 60;
  3619. Hour = (Bias - Minute) / 60;
  3620. UlTrace( LOGGING,
  3621. ("Ul!UlpGetGMTOffset: %c%02d:%02d (h:m) D/S %d BiasN %d\n",
  3622. Sign,
  3623. Hour,
  3624. Minute,
  3625. DT,
  3626. BiasN
  3627. ) );
  3628. _snprintf( g_GMTOffset,
  3629. SIZE_OF_GMT_OFFSET,
  3630. "%c%02d%02d",
  3631. Sign,
  3632. Hour,
  3633. Minute
  3634. );
  3635. }
  3636. /***************************************************************************++
  3637. Routine Description:
  3638. UlpInitializeLogBufferGranularity :
  3639. This will determine the (MAX) size of the buffer we will be using
  3640. for the eac log file entry.
  3641. --***************************************************************************/
  3642. NTSTATUS
  3643. UlpInitializeLogBufferGranularity()
  3644. {
  3645. SYSTEM_BASIC_INFORMATION sbi;
  3646. NTSTATUS Status;
  3647. Status = STATUS_SUCCESS;
  3648. //
  3649. // Get the granularity from the system
  3650. //
  3651. Status = NtQuerySystemInformation(
  3652. SystemBasicInformation,
  3653. (PVOID)&sbi,
  3654. sizeof(sbi),
  3655. NULL
  3656. );
  3657. if ( !NT_SUCCESS(Status) )
  3658. {
  3659. UlTrace( LOGGING,
  3660. ("Ul!UlpInitializeLogBufferGranularity: failure %08lx\n",
  3661. Status) );
  3662. return Status;
  3663. }
  3664. g_AllocationGranularity = sbi.AllocationGranularity;
  3665. UlTrace( LOGGING,
  3666. ("Ul!UlpInitializeLogBufferGranularity: %d\n",
  3667. g_AllocationGranularity
  3668. ) );
  3669. return Status;
  3670. }
  3671. /***************************************************************************++
  3672. Routine Description:
  3673. UlProbeLogField :
  3674. Probes the content of a user log field, including the terminating
  3675. null.
  3676. Arguments:
  3677. - the log field to be probed
  3678. --***************************************************************************/
  3679. __inline
  3680. VOID
  3681. UlpProbeLogField(
  3682. IN PVOID pField,
  3683. IN SIZE_T FieldLength,
  3684. IN ULONG Alignment
  3685. )
  3686. {
  3687. if ( pField )
  3688. {
  3689. ProbeTestForRead(
  3690. pField,
  3691. FieldLength + Alignment,
  3692. Alignment
  3693. );
  3694. }
  3695. }
  3696. /***************************************************************************++
  3697. Routine Description:
  3698. UlProbeLogData :
  3699. Probes the content of the user buffer of Log Data
  3700. Note: pUserLogData holds untrusted data sent down from user mode.
  3701. The caller MUST have a __try/__except block to catch any exceptions
  3702. or access violations that occur while probing this data.
  3703. Arguments:
  3704. PHTTP_LOG_FIELDS_DATA - The log data ( from WP ) to be probed and verified.
  3705. --***************************************************************************/
  3706. NTSTATUS
  3707. UlProbeLogData(
  3708. IN PHTTP_LOG_FIELDS_DATA pUserLogData
  3709. )
  3710. {
  3711. NTSTATUS Status;
  3712. PAGED_CODE();
  3713. Status = STATUS_SUCCESS;
  3714. if (pUserLogData)
  3715. {
  3716. UlTrace( LOGGING, ("Ul!UlProbeLogData: pUserLogData %p\n",
  3717. pUserLogData ));
  3718. //
  3719. // Check for the log fields data structure
  3720. //
  3721. ProbeTestForRead(
  3722. pUserLogData,
  3723. sizeof(HTTP_LOG_FIELDS_DATA),
  3724. sizeof(USHORT)
  3725. );
  3726. //
  3727. // Now check for the individual strings
  3728. //
  3729. UlpProbeLogField(pUserLogData->ClientIp,
  3730. pUserLogData->ClientIpLength,
  3731. sizeof(CHAR));
  3732. UlpProbeLogField(pUserLogData->ServiceName,
  3733. pUserLogData->ServiceNameLength,
  3734. sizeof(CHAR));
  3735. UlpProbeLogField(pUserLogData->ServerName,
  3736. pUserLogData->ServerNameLength,
  3737. sizeof(CHAR));
  3738. UlpProbeLogField(pUserLogData->ServerIp,
  3739. pUserLogData->ServerIpLength,
  3740. sizeof(CHAR));
  3741. UlpProbeLogField(pUserLogData->UriQuery,
  3742. pUserLogData->UriQueryLength,
  3743. sizeof(CHAR));
  3744. UlpProbeLogField(pUserLogData->Host,
  3745. pUserLogData->HostLength,
  3746. sizeof(CHAR));
  3747. UlpProbeLogField(pUserLogData->UserAgent,
  3748. pUserLogData->UserAgentLength,
  3749. sizeof(CHAR));
  3750. UlpProbeLogField(pUserLogData->Cookie,
  3751. pUserLogData->CookieLength,
  3752. sizeof(CHAR));
  3753. UlpProbeLogField(pUserLogData->Referrer,
  3754. pUserLogData->ReferrerLength,
  3755. sizeof(CHAR));
  3756. UlpProbeLogField(pUserLogData->Method,
  3757. pUserLogData->MethodLength,
  3758. sizeof(CHAR));
  3759. UlpProbeLogField(pUserLogData->UserName,
  3760. pUserLogData->UserNameLength,
  3761. sizeof(WCHAR));
  3762. UlpProbeLogField(pUserLogData->UriStem,
  3763. pUserLogData->UriStemLength,
  3764. sizeof(WCHAR));
  3765. #if DBG
  3766. // CODEWORK: should we do this all the time? Remember, this is
  3767. // untrusted user-mode data
  3768. //
  3769. // Few more controls for chk bits
  3770. //
  3771. if ((pUserLogData->ClientIp &&
  3772. pUserLogData->ClientIpLength != strlen(pUserLogData->ClientIp))
  3773. ||
  3774. (pUserLogData->ServiceName &&
  3775. pUserLogData->ServiceNameLength != strlen(pUserLogData->ServiceName))
  3776. ||
  3777. (pUserLogData->ServerName &&
  3778. pUserLogData->ServerNameLength != strlen(pUserLogData->ServerName))
  3779. ||
  3780. (pUserLogData->ServerIp &&
  3781. pUserLogData->ServerIpLength != strlen(pUserLogData->ServerIp))
  3782. ||
  3783. (pUserLogData->Method &&
  3784. pUserLogData->MethodLength != strlen(pUserLogData->Method))
  3785. ||
  3786. (pUserLogData->UriQuery &&
  3787. pUserLogData->UriQueryLength != strlen(pUserLogData->UriQuery))
  3788. ||
  3789. (pUserLogData->Host &&
  3790. pUserLogData->HostLength != strlen(pUserLogData->Host))
  3791. ||
  3792. (pUserLogData->UserAgent &&
  3793. pUserLogData->UserAgentLength != strlen(pUserLogData->UserAgent))
  3794. ||
  3795. (pUserLogData->Cookie &&
  3796. pUserLogData->CookieLength != strlen(pUserLogData->Cookie))
  3797. ||
  3798. (pUserLogData->Referrer &&
  3799. pUserLogData->ReferrerLength != strlen(pUserLogData->Referrer))
  3800. ||
  3801. (pUserLogData->UserName &&
  3802. pUserLogData->UserNameLength != wcslen(pUserLogData->UserName)*sizeof(WCHAR))
  3803. //
  3804. // Disabled because UriSTem may not be null terminated.
  3805. // ||
  3806. // (pUserLogData->UriStem &&
  3807. // pUserLogData->UriStemLength != wcslen(pUserLogData->UriStem)*sizeof(WCHAR))
  3808. )
  3809. {
  3810. //
  3811. // Invalid log field has been pushed down by the WP.
  3812. // Complain and reject the request.
  3813. //
  3814. UlTrace(LOGGING,(
  3815. "Ul!UlProbeLogData: INVALID field rcvd from WP in pLogFields %p \n",
  3816. pUserLogData
  3817. ));
  3818. Status = STATUS_INVALID_PARAMETER;
  3819. }
  3820. #endif // DBG
  3821. }
  3822. return Status;
  3823. }
  3824. /***************************************************************************++
  3825. Routine Description:
  3826. UlAllocateLogDataBuffer :
  3827. We capture the log fields from user ( WP ) buffer to our internal
  3828. buffer that we allocate here.
  3829. Also we set our pointer to pRequest here.
  3830. Arguments:
  3831. pLogData - The internal buffer to hold logging info. We will keep this
  3832. around until we are done with logging.
  3833. pRequest - Pointer to the currently logged request.
  3834. --***************************************************************************/
  3835. NTSTATUS
  3836. UlAllocateLogDataBuffer(
  3837. OUT PUL_LOG_DATA_BUFFER pLogData,
  3838. IN PUL_INTERNAL_REQUEST pRequest,
  3839. IN PUL_CONFIG_GROUP_OBJECT pConfigGroup // CG from cache or request
  3840. )
  3841. {
  3842. //
  3843. // Sanity check.
  3844. //
  3845. PAGED_CODE();
  3846. ASSERT(pLogData);
  3847. ASSERT(UL_IS_VALID_INTERNAL_REQUEST(pRequest));
  3848. ASSERT(IS_VALID_CONFIG_GROUP(pConfigGroup));
  3849. UlTrace(LOGGING, ("Ul!UlAllocateLogDataBuffer: pLogData %p \n",
  3850. pLogData
  3851. ));
  3852. //
  3853. // Initialize Log Fields in the Log Buffer
  3854. //
  3855. UL_REFERENCE_INTERNAL_REQUEST(pRequest);
  3856. pLogData->pRequest = pRequest;
  3857. pLogData->CacheAndSendResponse= FALSE;
  3858. pLogData->BytesTransferred= 0;
  3859. pLogData->pConfigGroup= NULL;
  3860. pLogData->Line = pLogData->Buffer;
  3861. //
  3862. // Capture Format & Flags from the Request's Config Group. Or
  3863. // from the cache entries.
  3864. // There's a possiblity that the values inside the log entry
  3865. // maybe stale but that's acceptable. We do not want to acquire
  3866. // the log resource so that we can avoid the contention.
  3867. //
  3868. pLogData->Format= pConfigGroup->LoggingConfig.LogFormat;
  3869. pLogData->Flags = UL_GET_LOG_TYPE_MASK(
  3870. pConfigGroup->LoggingConfig.LogFormat,
  3871. pConfigGroup->LoggingConfig.LogExtFileFlags
  3872. );
  3873. pLogData->Used= 0; // For NCSA & W3C this is size of the line.
  3874. pLogData->Length= 0; // Allocation length,the default is 4k
  3875. pLogData->UsedOffset1= 0; // Used by all formats.
  3876. pLogData->UsedOffset2= 0; // This SHOULD only be nonzero if format is IIS
  3877. return STATUS_SUCCESS;
  3878. }
  3879. /***************************************************************************++
  3880. Routine Description:
  3881. UlDestroyLogDataBuffer :
  3882. After we are done with writing this record we have to clean up
  3883. the internal log buffer here.
  3884. Arguments:
  3885. pLogData - The buffer to be destroyed
  3886. --***************************************************************************/
  3887. VOID
  3888. UlDestroyLogDataBufferWorker(
  3889. IN PUL_WORK_ITEM pWorkItem
  3890. )
  3891. {
  3892. PUL_LOG_DATA_BUFFER pLogData;
  3893. //
  3894. // Sanity check
  3895. //
  3896. ASSERT(pWorkItem);
  3897. pLogData = CONTAINING_RECORD(
  3898. pWorkItem,
  3899. UL_LOG_DATA_BUFFER,
  3900. WorkItem
  3901. );
  3902. //
  3903. // If we are keeping a private pointer to the cgroup release it
  3904. // as well
  3905. //
  3906. if (pLogData->pConfigGroup)
  3907. {
  3908. DEREFERENCE_CONFIG_GROUP(pLogData->pConfigGroup);
  3909. pLogData->pConfigGroup = NULL;
  3910. }
  3911. //
  3912. // Now release the possibly allocated large log line buffer
  3913. //
  3914. if (pLogData->Length > UL_LOG_LINE_BUFFER_SIZE)
  3915. {
  3916. // Large log line get allocated from paged pool
  3917. // we better be running on lowered IRQL for this case.
  3918. PAGED_CODE();
  3919. UL_FREE_POOL(pLogData->Line, UL_LOG_DATA_BUFFER_POOL_TAG);
  3920. }
  3921. //
  3922. // Last release our pointer to request structure here.
  3923. //
  3924. if (pLogData->pRequest)
  3925. {
  3926. PUL_INTERNAL_REQUEST pRequest;
  3927. pRequest = pLogData->pRequest;
  3928. pLogData->pRequest = NULL;
  3929. UL_DEREFERENCE_INTERNAL_REQUEST(pRequest);
  3930. }
  3931. UlTrace(LOGGING,("Ul!UlpDestroyLogDataBufferWorker: pLogData %p \n",
  3932. pLogData
  3933. ));
  3934. }
  3935. /***************************************************************************++
  3936. Routine Description:
  3937. Captures and writes the log fields from user (WP) buffer to the log line.
  3938. Captures only those necessary fields according to the picked Flags.
  3939. Does UTF8 and LocalCode Page conversion for UserName and URI Stem.
  3940. Leaves enough space for Date & Time fields for late generation.
  3941. Does SpaceToPlus conversion for UserAgent, Cookie, Referrer & Host.
  3942. Arguments:
  3943. pLogData : User Buffer which holds the fields and their lengths
  3944. Version : Version information from Request
  3945. pLogBuffer : Structure which holds final log line and additional
  3946. information.
  3947. --***************************************************************************/
  3948. NTSTATUS
  3949. UlCaptureLogFieldsW3C(
  3950. IN PHTTP_LOG_FIELDS_DATA pLogData,
  3951. IN HTTP_VERSION Version,
  3952. OUT PUL_LOG_DATA_BUFFER pLogBuffer
  3953. )
  3954. {
  3955. NTSTATUS Status;
  3956. ULONG Flags;
  3957. PCHAR psz;
  3958. PCHAR pBuffer;
  3959. ULONG BytesConverted;
  3960. ULONG FastLength;
  3961. //
  3962. // Sanity check.
  3963. //
  3964. PAGED_CODE();
  3965. ASSERT(pLogBuffer);
  3966. Status = STATUS_SUCCESS;
  3967. Flags = pLogBuffer->Flags;
  3968. BytesConverted = 0;
  3969. //
  3970. // Try fast length calculation for the the default case. If this fails
  3971. // Recalc function will precisely calculate the required length by
  3972. // paying attention to the picked flags.
  3973. //
  3974. FastLength = pLogData->ClientIpLength
  3975. + pLogData->UserNameLength
  3976. + pLogData->ServiceNameLength
  3977. + pLogData->ServerNameLength
  3978. + pLogData->ServerIpLength
  3979. + pLogData->MethodLength
  3980. + pLogData->UriStemLength
  3981. + pLogData->UriQueryLength
  3982. + pLogData->UserAgentLength
  3983. + pLogData->CookieLength
  3984. + pLogData->ReferrerLength
  3985. + pLogData->HostLength
  3986. + MAX_W3C_FIX_FIELD_OVERHEAD
  3987. ;
  3988. if (UTF8_LOGGING_ENABLED() || (FastLength > UL_LOG_LINE_BUFFER_SIZE))
  3989. {
  3990. FastLength = UlpCalcLogLineLengthW3C(
  3991. pLogData,
  3992. Flags,
  3993. (UTF8_LOGGING_ENABLED() ? 2 : 1)
  3994. );
  3995. if (FastLength > UL_LOG_LINE_BUFFER_SIZE)
  3996. {
  3997. Status = UlpReallocLogLine(pLogBuffer, FastLength);
  3998. if (!NT_SUCCESS(Status))
  3999. {
  4000. return Status;
  4001. }
  4002. }
  4003. }
  4004. //
  4005. // Leave enough space for the date & time fields: "2000-01-31 00:12:23 "
  4006. // For W3C format the field "Used" shows the size of the log line we have filled.
  4007. // UsedOffset1 shows how much space we have saved for date and/or time logfields.
  4008. // UsedOffset2 shows the size of the log line which will be stored in the cache
  4009. // entry, not including reserved space for date & time and the logfields after
  4010. // ServerPort. Therefore we only store the fragment which starts from logfield
  4011. // UserName to ServerPort. Others will be generated per cache hit.
  4012. //
  4013. psz = pBuffer = &pLogBuffer->Line[0];
  4014. if ( Flags & MD_EXTLOG_DATE ) psz += 11;
  4015. if ( Flags & MD_EXTLOG_TIME ) psz += 9;
  4016. // Generate all the fields except BytesSend, BytesReceived & TimeTaken
  4017. // They will be added when the send is complete and if they are picked
  4018. // We will only copy the fields after this point to the cache entry.
  4019. // Lets set and remember the size of the fields we are discarding.
  4020. pLogBuffer->UsedOffset1 = (USHORT) DIFF(psz - pBuffer);
  4021. if ( Flags & MD_EXTLOG_SITE_NAME )
  4022. {
  4023. if (pLogData->ServiceNameLength)
  4024. {
  4025. psz = UlStrPrintStr(psz, pLogData->ServiceName,' ');
  4026. }
  4027. else
  4028. {
  4029. *psz++ = '-'; *psz++ = ' ';
  4030. }
  4031. }
  4032. if ( Flags & MD_EXTLOG_COMPUTER_NAME )
  4033. {
  4034. if (pLogData->ServerNameLength)
  4035. {
  4036. psz = UlStrPrintStr(psz, pLogData->ServerName,' ');
  4037. }
  4038. else
  4039. {
  4040. *psz++ = '-'; *psz++ = ' ';
  4041. }
  4042. }
  4043. if ( Flags & MD_EXTLOG_SERVER_IP )
  4044. {
  4045. if (pLogData->ServerIpLength)
  4046. {
  4047. psz = UlStrPrintStr(psz, pLogData->ServerIp,' ');
  4048. }
  4049. else
  4050. {
  4051. *psz++ = '-'; *psz++ = ' ';
  4052. }
  4053. }
  4054. if ( Flags & MD_EXTLOG_METHOD )
  4055. {
  4056. if (pLogData->MethodLength)
  4057. {
  4058. psz = UlStrPrintStr(psz, pLogData->Method,' ');
  4059. }
  4060. else
  4061. {
  4062. *psz++ = '-'; *psz++ = ' ';
  4063. }
  4064. }
  4065. if ( Flags & MD_EXTLOG_URI_STEM )
  4066. {
  4067. BytesConverted = 0;
  4068. if (pLogData->UriStemLength)
  4069. {
  4070. PCHAR pszT = psz;
  4071. if (UTF8_LOGGING_ENABLED())
  4072. {
  4073. BytesConverted =
  4074. HttpUnicodeToUTF8(
  4075. pLogData->UriStem,
  4076. pLogData->UriStemLength/sizeof(WCHAR),
  4077. psz,
  4078. pLogData->UriStemLength * 2
  4079. );
  4080. ASSERT(BytesConverted);
  4081. }
  4082. else
  4083. {
  4084. RtlUnicodeToMultiByteN(
  4085. psz,
  4086. pLogData->UriStemLength,
  4087. &BytesConverted,
  4088. pLogData->UriStem,
  4089. pLogData->UriStemLength
  4090. );
  4091. }
  4092. psz += BytesConverted;
  4093. // Do SpeaceToPlus conversion before writting out
  4094. // the terminator space.
  4095. while (pszT != psz)
  4096. {
  4097. if (*pszT == ' ') *pszT = '+';
  4098. pszT++;
  4099. }
  4100. *psz++ = ' ';
  4101. }
  4102. else
  4103. {
  4104. *psz++ = '-'; *psz++ = ' ';
  4105. }
  4106. }
  4107. if ( Flags & MD_EXTLOG_URI_QUERY )
  4108. {
  4109. if (pLogData->UriQueryLength)
  4110. {
  4111. psz = UlStrPrintStr(psz, pLogData->UriQuery,' ');
  4112. }
  4113. else
  4114. {
  4115. *psz++ = '-'; *psz++ = ' ';
  4116. }
  4117. }
  4118. if ( Flags & MD_EXTLOG_HTTP_STATUS )
  4119. {
  4120. psz = UlStrPrintProtocolStatus(psz,(USHORT)pLogData->ProtocolStatus,' ');
  4121. }
  4122. if ( Flags & MD_EXTLOG_WIN32_STATUS )
  4123. {
  4124. psz = UlStrPrintUlong(psz, pLogData->Win32Status,' ');
  4125. }
  4126. if ( Flags & MD_EXTLOG_SERVER_PORT )
  4127. {
  4128. psz = UlStrPrintUlong(psz, pLogData->ServerPort,' ');
  4129. }
  4130. //
  4131. // Cache builder won't be storing the fields after this line.
  4132. // They have to be generated per hit for cache hits.
  4133. //
  4134. pLogBuffer->UsedOffset2 = (USHORT) DIFF(psz - pBuffer);
  4135. if ( Flags & MD_EXTLOG_USERNAME )
  4136. {
  4137. BytesConverted = 0;
  4138. if (pLogData->UserNameLength)
  4139. {
  4140. // Do either UTF8 or LocalCodePage Conversion
  4141. // not including the terminating null.
  4142. PCHAR pszT = psz;
  4143. if (UTF8_LOGGING_ENABLED())
  4144. {
  4145. // UTF8 Conversion may require upto two times because of a
  4146. // possible 2 byte to 4 byte conversion.
  4147. BytesConverted =
  4148. HttpUnicodeToUTF8(
  4149. pLogData->UserName,
  4150. pLogData->UserNameLength/sizeof(WCHAR),
  4151. psz,
  4152. pLogData->UserNameLength * 2
  4153. );
  4154. ASSERT(BytesConverted);
  4155. }
  4156. else
  4157. {
  4158. // Local codepage is normally closer to the half the length,
  4159. // but due to the possibility of pre-composed characters,
  4160. // the upperbound of the ANSI length is the UNICODE length
  4161. // in bytes
  4162. RtlUnicodeToMultiByteN(
  4163. psz,
  4164. pLogData->UserNameLength,
  4165. &BytesConverted,
  4166. pLogData->UserName,
  4167. pLogData->UserNameLength
  4168. );
  4169. }
  4170. // Forward the psz by BytesConverted
  4171. psz += BytesConverted;
  4172. // Do SpaceToPlus conversion
  4173. while (pszT != psz)
  4174. {
  4175. if (*pszT == ' ') *pszT = '+';
  4176. pszT++;
  4177. }
  4178. *psz++ = ' ';
  4179. }
  4180. else
  4181. {
  4182. *psz++ = '-'; *psz++ = ' ';
  4183. }
  4184. }
  4185. if ( Flags & MD_EXTLOG_CLIENT_IP )
  4186. {
  4187. if (pLogData->ClientIpLength)
  4188. {
  4189. psz = UlStrPrintStr(psz, pLogData->ClientIp,' ');
  4190. }
  4191. else
  4192. {
  4193. *psz++ = '-'; *psz++ = ' ';
  4194. }
  4195. }
  4196. if ( Flags & MD_EXTLOG_PROTOCOL_VERSION )
  4197. {
  4198. psz = UlStrPrintStr(psz, UL_GET_NAME_FOR_HTTP_VERSION(Version),' ');
  4199. }
  4200. if ( Flags & MD_EXTLOG_USER_AGENT )
  4201. {
  4202. if (pLogData->UserAgentLength)
  4203. {
  4204. psz = UlStrPrintStrC(psz, pLogData->UserAgent,' ');
  4205. }
  4206. else
  4207. {
  4208. *psz++ = '-'; *psz++ = ' ';
  4209. }
  4210. }
  4211. if ( Flags & MD_EXTLOG_COOKIE )
  4212. {
  4213. if (pLogData->CookieLength)
  4214. {
  4215. psz = UlStrPrintStrC(psz, pLogData->Cookie,' ');
  4216. }
  4217. else
  4218. {
  4219. *psz++ = '-'; *psz++ = ' ';
  4220. }
  4221. }
  4222. if ( Flags & MD_EXTLOG_REFERER )
  4223. {
  4224. if (pLogData->ReferrerLength)
  4225. {
  4226. psz = UlStrPrintStrC(psz, pLogData->Referrer,' ');
  4227. }
  4228. else
  4229. {
  4230. *psz++ = '-'; *psz++ = ' ';
  4231. }
  4232. }
  4233. if ( Flags & MD_EXTLOG_HOST )
  4234. {
  4235. if (pLogData->HostLength)
  4236. {
  4237. psz = UlStrPrintStrC(psz, pLogData->Host,' ');
  4238. }
  4239. else
  4240. {
  4241. *psz++ = '-'; *psz++ = ' ';
  4242. }
  4243. }
  4244. // Finally calculate the used space
  4245. pLogBuffer->Used = (ULONG) DIFF(psz - pBuffer);
  4246. // Date & Time fields will be filled in when the LogHit completes.
  4247. // As well as the fields BytesSent,BytesReceived and TimeTaken will
  4248. // be added to the end of the log line, then.
  4249. UlTrace(LOGGING, ("Ul!UlCaptureLogFields: user %p kernel %p\n",
  4250. pLogData,pLogBuffer
  4251. ));
  4252. return Status;
  4253. }
  4254. NTSTATUS
  4255. UlCaptureLogFieldsNCSA(
  4256. IN PHTTP_LOG_FIELDS_DATA pLogData,
  4257. IN HTTP_VERSION Version,
  4258. OUT PUL_LOG_DATA_BUFFER pLogBuffer
  4259. )
  4260. {
  4261. NTSTATUS Status;
  4262. ULONG BytesConverted;
  4263. PCHAR psz;
  4264. PCHAR pBuffer;
  4265. ULONG Utf8Multiplier;
  4266. //
  4267. // Sanity check.
  4268. //
  4269. PAGED_CODE();
  4270. ASSERT(pLogBuffer);
  4271. Status = STATUS_SUCCESS;
  4272. Utf8Multiplier = (UTF8_LOGGING_ENABLED() ? 2 : 1);
  4273. // Estimate the length and reallocate the log data buffer line
  4274. // if necessary
  4275. pLogBuffer->Length =
  4276. 2 + pLogData->ClientIpLength +
  4277. 2 + // For remote user log name
  4278. 2 + pLogData->UserNameLength * Utf8Multiplier +
  4279. 29 + // Fixed Date & Time Space
  4280. 2 + pLogData->MethodLength + // "MTHD U-STEM?U-QUERY P-VER"
  4281. 2 + pLogData->UriStemLength * Utf8Multiplier +
  4282. 2 + pLogData->UriQueryLength +
  4283. 2 + UL_HTTP_VERSION_LENGTH + // Version plus Quotes ""
  4284. 2 + MAX_ULONG_STR + // ProtocolStatus
  4285. 2 + MAX_ULONGLONG_STR + // BytesSend
  4286. 3 // \r\n\0
  4287. ;
  4288. if ( pLogBuffer->Length > UL_LOG_LINE_BUFFER_SIZE )
  4289. {
  4290. Status = UlpReallocLogLine(pLogBuffer, pLogBuffer->Length);
  4291. if (!NT_SUCCESS(Status))
  4292. {
  4293. return Status;
  4294. }
  4295. }
  4296. //
  4297. // UCIP - username [date:time offset] "MTHD U-STEM?U-QUERY P-VER" Status BSent
  4298. //
  4299. // Set and remember the beginning
  4300. psz = pBuffer = &pLogBuffer->Line[0];
  4301. // Client IP
  4302. if (pLogData->ClientIpLength)
  4303. {
  4304. psz = UlStrPrintStr(psz, pLogData->ClientIp,' ');
  4305. }
  4306. else
  4307. {
  4308. *psz++ = '-'; *psz++ = ' ';
  4309. }
  4310. // Fixed dash
  4311. *psz++ = '-'; *psz++ = ' ';
  4312. // UserName
  4313. if (pLogData->UserNameLength)
  4314. {
  4315. BytesConverted = 0;
  4316. if (UTF8_LOGGING_ENABLED())
  4317. {
  4318. BytesConverted =
  4319. HttpUnicodeToUTF8(
  4320. pLogData->UserName,
  4321. pLogData->UserNameLength/sizeof(WCHAR),
  4322. psz,
  4323. pLogData->UserNameLength * 2
  4324. );
  4325. ASSERT(BytesConverted);
  4326. }
  4327. else
  4328. {
  4329. RtlUnicodeToMultiByteN(
  4330. psz,
  4331. pLogData->UserNameLength,
  4332. &BytesConverted,
  4333. pLogData->UserName,
  4334. pLogData->UserNameLength
  4335. );
  4336. }
  4337. psz += BytesConverted; *psz++ = ' ';
  4338. }
  4339. else
  4340. {
  4341. *psz++ = '-'; *psz++ = ' ';
  4342. }
  4343. // [Date:Time GmtOffset] -> "[07/Jan/2000:00:02:23 -0800] "
  4344. // Just leave the space for the time being. But remember the
  4345. // offset of the beginning of the reserved space of 29 bytes.
  4346. pLogBuffer->UsedOffset1 = (USHORT) DIFF(psz - pBuffer);
  4347. // Forward psz to bypass the reserved space
  4348. psz += NCSA_FIX_DATE_AND_TIME_FIELD_SIZE;
  4349. // "MTHD U-STEM?U-QUERY P-VER"
  4350. *psz++ = '\"';
  4351. if (pLogData->MethodLength)
  4352. {
  4353. psz = UlStrPrintStr(psz, pLogData->Method,' ');
  4354. }
  4355. else
  4356. {
  4357. *psz++ = '-'; *psz++ = ' ';
  4358. }
  4359. if (pLogData->UriStemLength)
  4360. {
  4361. BytesConverted = 0;
  4362. if (UTF8_LOGGING_ENABLED())
  4363. {
  4364. BytesConverted =
  4365. HttpUnicodeToUTF8(
  4366. pLogData->UriStem,
  4367. pLogData->UriStemLength/sizeof(WCHAR),
  4368. psz,
  4369. pLogData->UriStemLength * 2
  4370. );
  4371. ASSERT(BytesConverted);
  4372. }
  4373. else
  4374. {
  4375. RtlUnicodeToMultiByteN(
  4376. psz,
  4377. pLogData->UriStemLength,
  4378. &BytesConverted,
  4379. pLogData->UriStem,
  4380. pLogData->UriStemLength
  4381. );
  4382. }
  4383. psz += BytesConverted;
  4384. }
  4385. else
  4386. {
  4387. *psz++ = '-';
  4388. }
  4389. if (pLogData->UriQueryLength)
  4390. {
  4391. *psz++ = '?';
  4392. psz = UlStrPrintStr(psz, pLogData->UriQuery,' ');
  4393. }
  4394. else
  4395. {
  4396. *psz++ = ' ';
  4397. }
  4398. pLogBuffer->UsedOffset2 = (USHORT) DIFF(psz - pBuffer);
  4399. psz = UlStrPrintStr(psz, UL_GET_NAME_FOR_HTTP_VERSION(Version),'\"');
  4400. *psz++ = ' ';
  4401. // ProtocolStatus
  4402. psz = UlStrPrintProtocolStatus(psz, (USHORT)pLogData->ProtocolStatus,' ');
  4403. // Calculate the length upto now
  4404. pLogBuffer->Used += (ULONG) DIFF(psz - pBuffer);
  4405. // BytesSend will be filled later on during hit processing
  4406. return Status;
  4407. }
  4408. NTSTATUS
  4409. UlCaptureLogFieldsIIS(
  4410. IN PHTTP_LOG_FIELDS_DATA pLogData,
  4411. IN HTTP_VERSION Version,
  4412. OUT PUL_LOG_DATA_BUFFER pLogBuffer
  4413. )
  4414. {
  4415. NTSTATUS Status;
  4416. ULONG BytesConverted;
  4417. PCHAR psz;
  4418. PCHAR pBuffer;
  4419. ULONG Utf8Multiplier;
  4420. //
  4421. // Sanity check.
  4422. //
  4423. PAGED_CODE();
  4424. ASSERT(pLogBuffer);
  4425. Status = STATUS_SUCCESS;
  4426. Utf8Multiplier = (UTF8_LOGGING_ENABLED() ? 2 : 1);
  4427. // Estimate the length and reallocate the log data buffer line
  4428. // if necessary
  4429. pLogBuffer->Length =
  4430. 2 + pLogData->ClientIpLength +
  4431. 2 + pLogData->UserNameLength * Utf8Multiplier +
  4432. 22 + // Fixed Date & Time Space
  4433. 2 + pLogData->ServiceNameLength +
  4434. 2 + pLogData->ServerNameLength +
  4435. 2 + pLogData->ServerIpLength +
  4436. 2 + MAX_ULONGLONG_STR + // TimeTaken
  4437. 2 + MAX_ULONGLONG_STR + // BytesReceived
  4438. 2 + MAX_ULONGLONG_STR + // BytesSend
  4439. 2 + MAX_ULONG_STR + // ProtocolStatus
  4440. 2 + MAX_ULONG_STR + // Win32 Status
  4441. 2 + pLogData->MethodLength +
  4442. 2 + pLogData->UriStemLength * Utf8Multiplier +
  4443. 2 + pLogData->UriQueryLength +
  4444. 3 // \r\n\0
  4445. ;
  4446. if ( pLogBuffer->Length > UL_LOG_LINE_BUFFER_SIZE )
  4447. {
  4448. // TODO: Fix this and do the limit checks per field
  4449. if (pLogBuffer->Length > UL_MAX_LOG_LINE_BUFFER_SIZE)
  4450. {
  4451. return STATUS_INVALID_PARAMETER;
  4452. }
  4453. Status = UlpReallocLogLine(pLogBuffer, UL_MAX_LOG_LINE_BUFFER_SIZE);
  4454. if (!NT_SUCCESS(Status))
  4455. {
  4456. return Status;
  4457. }
  4458. }
  4459. //
  4460. // UIP,user,D,T,site,Server,SIP,Ttaken,BR,BS,PS,WS,M,URI,URIQUERY,
  4461. //
  4462. // We will store the fragmented IIS log line as follows. If captured fields
  4463. // won't fit to the default 4k buffer the fragments will be increased by mul
  4464. // tiplier of 2 until they fit. But this calculation happened earlier and now
  4465. // we know the actual size of each fragment. All the available fields will be
  4466. // written to the beginning of their corresponding fragments and unavailable ones
  4467. // for the first two fragments ( i.e. date and time for 1 and timetaken
  4468. // bytesreceived and bytessent for 2, none for the three ) will be appended later
  4469. // when the hit happens. To be able to do that we will remember used_offsets for
  4470. // fragments one and two, both in the log_data and in the cache entry. But for this
  4471. // schema we need a special LogWriter which expects and handles 3 fragments rather
  4472. // than one complete line.
  4473. // <- upto time -> <- from siteName to BytesSent -> <- From p status to query ->
  4474. // 0 511 512 1023 1024 4096
  4475. // FRAGMENT ONE
  4476. // -----------------------------------------------------
  4477. // Cache entry will not store this fragment, the fields in
  4478. // here have to be regenerated for the (pure) cache hits.
  4479. psz = pBuffer = &pLogBuffer->Line[0];
  4480. // Client IP
  4481. if (pLogData->ClientIpLength)
  4482. {
  4483. psz = UlStrPrintStr(psz, pLogData->ClientIp,',');
  4484. }
  4485. else
  4486. {
  4487. *psz++ = '-'; *psz++ = ',';
  4488. }
  4489. *psz++ = ' ';
  4490. // UserName
  4491. if (pLogData->UserNameLength)
  4492. {
  4493. BytesConverted = 0;
  4494. if (UTF8_LOGGING_ENABLED())
  4495. {
  4496. BytesConverted =
  4497. HttpUnicodeToUTF8(
  4498. pLogData->UserName,
  4499. pLogData->UserNameLength/sizeof(WCHAR),
  4500. psz,
  4501. pLogData->UserNameLength * 2
  4502. );
  4503. ASSERT(BytesConverted);
  4504. }
  4505. else
  4506. {
  4507. RtlUnicodeToMultiByteN(
  4508. psz,
  4509. pLogData->UserNameLength,
  4510. &BytesConverted,
  4511. pLogData->UserName,
  4512. pLogData->UserNameLength
  4513. );
  4514. }
  4515. psz += BytesConverted; *psz++ = ',';
  4516. }
  4517. else
  4518. {
  4519. *psz++ = '-'; *psz++ = ',';
  4520. }
  4521. *psz++ = ' ';
  4522. // Date and Time will be added later to the end of this fragment.
  4523. pLogBuffer->UsedOffset1 = (USHORT) DIFF(psz - pBuffer);
  4524. // FRAGMENT TWO
  4525. // -----------------------------------------------------
  4526. pBuffer = psz = &pLogBuffer->Line[512];
  4527. // SiteName
  4528. if (pLogData->ServiceNameLength)
  4529. {
  4530. psz = UlStrPrintStr(psz, pLogData->ServiceName,',');
  4531. }
  4532. else
  4533. {
  4534. *psz++ = '-'; *psz++ = ',';
  4535. }
  4536. *psz++ = ' ';
  4537. // ServerName
  4538. if (pLogData->ServerNameLength)
  4539. {
  4540. psz = UlStrPrintStr(psz, pLogData->ServerName,',');
  4541. }
  4542. else
  4543. {
  4544. *psz++ = '-'; *psz++ = ',';
  4545. }
  4546. *psz++ = ' ';
  4547. // ServerIp
  4548. if (pLogData->ServerIpLength)
  4549. {
  4550. psz = UlStrPrintStr(psz, pLogData->ServerIp,',');
  4551. }
  4552. else
  4553. {
  4554. *psz++ = '-'; *psz++ = ',';
  4555. }
  4556. *psz++ = ' ';
  4557. // TimeTaken BytesSent and BytesReceived will be added later
  4558. // to the end of this fragment
  4559. pLogBuffer->UsedOffset2 = (USHORT) DIFF(psz - pBuffer);
  4560. // FRAGMENT THREE
  4561. // -----------------------------------------------------
  4562. pBuffer = psz = &pLogBuffer->Line[1024];
  4563. // ProtocolStatus
  4564. psz = UlStrPrintProtocolStatus(psz, (USHORT)pLogData->ProtocolStatus,',');
  4565. *psz++ = ' ';
  4566. // Win32 Status
  4567. psz = UlStrPrintUlong(psz, pLogData->Win32Status,',');
  4568. *psz++ = ' ';
  4569. // Method
  4570. if (pLogData->MethodLength)
  4571. {
  4572. psz = UlStrPrintStr(psz, pLogData->Method,',');
  4573. }
  4574. else
  4575. {
  4576. *psz++ = '-'; *psz++ = ',';
  4577. }
  4578. *psz++ = ' ';
  4579. // URI Stem
  4580. if (pLogData->UriStemLength)
  4581. {
  4582. BytesConverted = 0;
  4583. if (UTF8_LOGGING_ENABLED())
  4584. {
  4585. BytesConverted =
  4586. HttpUnicodeToUTF8(
  4587. pLogData->UriStem,
  4588. pLogData->UriStemLength/sizeof(WCHAR),
  4589. psz,
  4590. pLogData->UriStemLength * 2
  4591. );
  4592. ASSERT(BytesConverted);
  4593. }
  4594. else
  4595. {
  4596. RtlUnicodeToMultiByteN(
  4597. psz,
  4598. pLogData->UriStemLength,
  4599. &BytesConverted,
  4600. pLogData->UriStem,
  4601. pLogData->UriStemLength
  4602. );
  4603. }
  4604. psz += BytesConverted; *psz++ = ',';
  4605. }
  4606. else
  4607. {
  4608. *psz++ = '-'; *psz++ = ',';
  4609. }
  4610. *psz++ = ' ';
  4611. // URI Query
  4612. if (pLogData->UriQueryLength)
  4613. {
  4614. psz = UlStrPrintStr(psz, pLogData->UriQuery,',');
  4615. }
  4616. else
  4617. {
  4618. *psz++ = '-'; *psz++ = ',';
  4619. }
  4620. *psz++ = '\r'; *psz++ = '\n';
  4621. // The size of the fragment 3 goes to Used
  4622. pLogBuffer->Used += (ULONG) DIFF(psz - pBuffer);
  4623. *psz++ = ANSI_NULL;
  4624. return Status;
  4625. }
  4626. /***************************************************************************++
  4627. Routine Description:
  4628. UlLogHttpHit :
  4629. This function ( or its cache pair ) gets called everytime a log hit
  4630. happens. Just before completing the SendResponse request to the user.
  4631. The most likely places for calling this API or its pair for cache
  4632. is just before the send completion when we were about the destroy
  4633. send trackers.
  4634. Means:
  4635. 1. UlpCompleteSendRequestWorker for ORDINARY hits; before destroying
  4636. the PUL_CHUNK_TRACKER for send operation.
  4637. 2. UlpCompleteSendCacheEntryWorker for both types of CACHE hits
  4638. (cache build&send or just pure cache hit) before destroying the
  4639. the PUL_FULL_TRACKER for cache send operation.
  4640. This function requires Request & Response structures ( whereas its
  4641. cache pair only requires the Request ) to successfully generate the
  4642. the log fields and even for referencing to the right log configuration
  4643. settings for this site ( thru pRequest's pConfigInfo pointer ).
  4644. Unfortunately the major concern is untimely resetting of the connection
  4645. (when client terminates the connection before we get a chance to reach
  4646. to the -above- mentioned places) In that case UlConnectionDestroyedWorker
  4647. will asynchrously destroy our pRequest pointer ( in the pHttpConnection
  4648. structure ) and cause us to miss log hits. So we cannot trust on
  4649. HttpConnection for pRequest pointer. To solve this issue we keeep our
  4650. own pointer to Request. No need to worry about pResponse as it's going
  4651. to be preserved by chunk_trucker until its own destruction.
  4652. Arguments:
  4653. pResponse - pointer to the internal response structure, surely
  4654. passed down by the chunk tracker. We will grap our Log Data
  4655. buffer from this structure which originaly allocated when
  4656. capturing the Response from the user.
  4657. See UlCaptureHttpResponse.
  4658. --***************************************************************************/
  4659. NTSTATUS
  4660. UlLogHttpHit(
  4661. IN PUL_LOG_DATA_BUFFER pLogBuffer
  4662. )
  4663. {
  4664. NTSTATUS Status;
  4665. PUL_CONFIG_GROUP_OBJECT pConfigGroup;
  4666. PUL_INTERNAL_REQUEST pRequest;
  4667. PCHAR psz;
  4668. PCHAR pBuffer;
  4669. ULONG BytesWritten;
  4670. LONGLONG LifeTime;
  4671. LARGE_INTEGER CurrentTimeStamp;
  4672. PUL_LOG_FILE_ENTRY pEntry;
  4673. //
  4674. // A LOT of sanity checks.
  4675. //
  4676. PAGED_CODE();
  4677. Status = STATUS_SUCCESS;
  4678. UlTrace( LOGGING, ("Ul!UlLogHttpHit: pLogData %p\n", pLogBuffer ));
  4679. ASSERT(pLogBuffer);
  4680. pRequest = pLogBuffer->pRequest;
  4681. ASSERT(UL_IS_VALID_INTERNAL_REQUEST(pRequest));
  4682. if (pRequest->ConfigInfo.pLoggingConfig == NULL ||
  4683. IS_LOGGING_DISABLED(pRequest->ConfigInfo.pLoggingConfig)
  4684. )
  4685. {
  4686. //
  4687. // If logging is disabled or log settings don't
  4688. // exist then do not proceed. Just exit out.
  4689. //
  4690. return STATUS_SUCCESS;
  4691. }
  4692. pConfigGroup = pRequest->ConfigInfo.pLoggingConfig;
  4693. ASSERT(IS_VALID_CONFIG_GROUP(pConfigGroup));
  4694. //
  4695. // Construct the remaining log fields
  4696. //
  4697. switch(pLogBuffer->Format)
  4698. {
  4699. case HttpLoggingTypeW3C:
  4700. {
  4701. // First write the date & time fields to the beginning reserved
  4702. // space. Do not increment the used counter for date & time, b/c
  4703. // CaptureLogFields already did this when reserving the space.
  4704. psz = &pLogBuffer->Line[0];
  4705. if ( pLogBuffer->Flags & MD_EXTLOG_DATE )
  4706. {
  4707. UlpGetDateTimeFields(
  4708. HttpLoggingTypeW3C,
  4709. psz,
  4710. &BytesWritten,
  4711. NULL,
  4712. NULL
  4713. );
  4714. psz += BytesWritten; *psz++ = ' ';
  4715. ASSERT(BytesWritten == 10);
  4716. }
  4717. if ( pLogBuffer->Flags & MD_EXTLOG_TIME )
  4718. {
  4719. UlpGetDateTimeFields(
  4720. HttpLoggingTypeW3C,
  4721. NULL,
  4722. NULL,
  4723. psz,
  4724. &BytesWritten
  4725. );
  4726. psz += BytesWritten; *psz++ = ' ';
  4727. ASSERT(BytesWritten == 8);
  4728. }
  4729. // Set and remember where we started
  4730. pBuffer = psz = &pLogBuffer->Line[pLogBuffer->Used];
  4731. // Now proceed with the remaining fields, but add them to the end.
  4732. if ( pLogBuffer->Flags & MD_EXTLOG_BYTES_SENT )
  4733. {
  4734. psz = UlStrPrintUlonglong(psz, pRequest->BytesSent,' ');
  4735. }
  4736. if ( pLogBuffer->Flags & MD_EXTLOG_BYTES_RECV )
  4737. {
  4738. psz = UlStrPrintUlonglong(psz, pRequest->BytesReceived,' ');
  4739. }
  4740. if ( pLogBuffer->Flags & MD_EXTLOG_TIME_TAKEN )
  4741. {
  4742. KeQuerySystemTime( &CurrentTimeStamp );
  4743. LifeTime = CurrentTimeStamp.QuadPart - pRequest->TimeStamp.QuadPart;
  4744. if (LifeTime < 0)
  4745. {
  4746. LifeTime = 0;
  4747. UlTrace(LOGGING, ("CopyTimeStampField failure.\n"));
  4748. }
  4749. LifeTime /= (10*1000); // Conversion from 100-nanosecond to millisecs.
  4750. psz = UlStrPrintUlonglong(psz, (ULONGLONG)LifeTime,' ');
  4751. }
  4752. // Now calculate the space we have used
  4753. pLogBuffer->Used += (ULONG) DIFF(psz - pBuffer);
  4754. // Eat the last space and write the \r\n to the end.
  4755. // Only if we have any fields picked and written
  4756. if (pLogBuffer->Used)
  4757. {
  4758. psz = &pLogBuffer->Line[pLogBuffer->Used-1]; // Eat the last space
  4759. *psz++ = '\r'; *psz++ = '\n'; *psz++ = ANSI_NULL;
  4760. pLogBuffer->Used += 1;
  4761. }
  4762. else
  4763. {
  4764. return STATUS_SUCCESS; // No log fields nothing to log
  4765. }
  4766. // Cleanup the UsedOffsets otherwise length calculation will
  4767. // fail down below.
  4768. pLogBuffer->UsedOffset1 = pLogBuffer->UsedOffset2 = 0;
  4769. }
  4770. break;
  4771. case HttpLoggingTypeNCSA:
  4772. {
  4773. // [date:time GmtOffset] -> "[07/Jan/2000:00:02:23 -0800] "
  4774. // Restore the pointer to the reserved space first.
  4775. psz = &pLogBuffer->Line[pLogBuffer->UsedOffset1];
  4776. *psz++ = '[';
  4777. UlpGetDateTimeFields(
  4778. HttpLoggingTypeNCSA,
  4779. psz,
  4780. &BytesWritten,
  4781. NULL,
  4782. NULL
  4783. );
  4784. psz += BytesWritten; *psz++ = ':';
  4785. ASSERT(BytesWritten == 11);
  4786. UlpGetDateTimeFields(
  4787. HttpLoggingTypeNCSA,
  4788. NULL,
  4789. NULL,
  4790. psz,
  4791. &BytesWritten
  4792. );
  4793. psz += BytesWritten; *psz++ = ' ';
  4794. ASSERT(BytesWritten == 8);
  4795. UlAcquireResourceShared(&g_pUlNonpagedData->LogListResource, TRUE);
  4796. psz = UlStrPrintStr(psz, g_GMTOffset,']');
  4797. UlReleaseResource(&g_pUlNonpagedData->LogListResource);
  4798. *psz++ = ' ';
  4799. ASSERT(((ULONG) DIFF(psz - &pLogBuffer->Line[pLogBuffer->UsedOffset1])) == 29);
  4800. // BytesSent
  4801. pBuffer = psz = &pLogBuffer->Line[pLogBuffer->Used];
  4802. psz = UlStrPrintUlonglong(psz, pRequest->BytesSent,'\r');
  4803. pLogBuffer->Used += (ULONG) DIFF(psz - pBuffer);
  4804. // \n\0
  4805. *psz++ = '\n'; *psz++ = ANSI_NULL;
  4806. pLogBuffer->Used += 1;
  4807. // Cleanup the UsedOffsets otherwise length calculation will
  4808. // fail down below.
  4809. pLogBuffer->UsedOffset1 = pLogBuffer->UsedOffset2 = 0;
  4810. }
  4811. break;
  4812. case HttpLoggingTypeIIS:
  4813. {
  4814. // At this time to construct the IIS log line is slightly
  4815. // different than the cache completion case. We can either
  4816. // use memmoves to get rid of the gaps between the three
  4817. // fragments. Or we provide a special log writer to actually
  4818. // handle three fragments when writting to the final log buffer
  4819. // I have picked the latter.
  4820. // Complete Fragment 1
  4821. pBuffer = psz = &pLogBuffer->Line[pLogBuffer->UsedOffset1];
  4822. UlpGetDateTimeFields(
  4823. HttpLoggingTypeIIS,
  4824. psz,
  4825. &BytesWritten,
  4826. NULL,
  4827. NULL
  4828. );
  4829. psz += BytesWritten; *psz++ = ','; *psz++ = ' ';
  4830. UlpGetDateTimeFields(
  4831. HttpLoggingTypeIIS,
  4832. NULL,
  4833. NULL,
  4834. psz,
  4835. &BytesWritten
  4836. );
  4837. psz += BytesWritten; *psz++ = ','; *psz++ = ' ';
  4838. pLogBuffer->UsedOffset1 += (USHORT) DIFF(psz - pBuffer);
  4839. // Complete Fragment 2
  4840. pBuffer = psz = &pLogBuffer->Line[512 + pLogBuffer->UsedOffset2];
  4841. KeQuerySystemTime( &CurrentTimeStamp );
  4842. LifeTime = CurrentTimeStamp.QuadPart - pRequest->TimeStamp.QuadPart;
  4843. if (LifeTime < 0)
  4844. {
  4845. LifeTime = 0;
  4846. UlTrace(LOGGING, ("CopyTimeStampField: failure.\n"));
  4847. }
  4848. LifeTime /= (10*1000); // Conversion from 100-nanosecond to millisecs.
  4849. psz = UlStrPrintUlonglong(psz, (ULONGLONG)LifeTime,',');
  4850. *psz++ = ' ';
  4851. psz = UlStrPrintUlonglong(psz, pRequest->BytesReceived,',');
  4852. *psz++ = ' ';
  4853. psz = UlStrPrintUlonglong(psz, pRequest->BytesSent,',');
  4854. *psz++ = ' ';
  4855. pLogBuffer->UsedOffset2 += (USHORT) DIFF(psz - pBuffer);
  4856. // Size of the final log line should be
  4857. // pLogBuffer->UsedOffset1 + pLogBuffer->UsedOffset2 + pLogBuffer->Used
  4858. // for IIS log format down below.
  4859. }
  4860. break;
  4861. default:
  4862. {
  4863. ASSERT(!"Unknown Log Format Type\n");
  4864. return STATUS_INVALID_PARAMETER;
  4865. }
  4866. }
  4867. //
  4868. // Finally this log line is ready to rock. Lets write it out.
  4869. //
  4870. UlAcquireResourceShared(&g_pUlNonpagedData->LogListResource, TRUE);
  4871. //
  4872. // Unless otherwise there's a problem in the set config group Ioctl
  4873. // we should never come here with having a null entry pointer. Even
  4874. // in the case of log reconfiguration, cgroup should acquire the log
  4875. // list resource to update its entry pointer.
  4876. //
  4877. pEntry = pConfigGroup->pLogFileEntry;
  4878. if (pEntry)
  4879. {
  4880. ASSERT(IS_VALID_LOG_FILE_ENTRY(pEntry));
  4881. Status =
  4882. UlpWriteToLogFile (
  4883. pEntry,
  4884. pLogBuffer->UsedOffset1 + pLogBuffer->UsedOffset2 + pLogBuffer->Used,
  4885. &pLogBuffer->Line[0],
  4886. pLogBuffer->UsedOffset1,
  4887. pLogBuffer->UsedOffset2
  4888. );
  4889. }
  4890. #if DBG
  4891. else
  4892. {
  4893. //
  4894. // Practically every time we leak a url_cache entry we will not close
  4895. // the corresponding cgroup and the corresponding log entry. In that
  4896. // case next time we try to run http.sys we might see this assertion
  4897. // going on. Not usefull at this time because it's too late to cacth
  4898. // the original leak. Also if we run out of system resources when
  4899. // allocation the log file entry we will come here as well.
  4900. //
  4901. UlTrace(LOGGING,("Ul!UlLogHttpHit:null logfile entry !\n"));
  4902. Status = STATUS_INVALID_PARAMETER;
  4903. }
  4904. if (!NT_SUCCESS(Status))
  4905. {
  4906. UlTrace( LOGGING, ("Ul!UlLogHttpHit: entry %p, failure %08lx \n",
  4907. pEntry,
  4908. Status
  4909. ));
  4910. }
  4911. #endif // DBG
  4912. UlReleaseResource(&g_pUlNonpagedData->LogListResource);
  4913. return Status;
  4914. }
  4915. /***************************************************************************++
  4916. Routine Description:
  4917. Queues a work item for the actual log hit function.
  4918. Arguments:
  4919. pTracker - Supplies the tracker to complete.
  4920. --***************************************************************************/
  4921. NTSTATUS
  4922. UlLogHttpCacheHit(
  4923. PUL_FULL_TRACKER pTracker
  4924. )
  4925. {
  4926. NTSTATUS Status;
  4927. PUL_LOG_DATA_BUFFER pLogData;
  4928. ULONG NewLength;
  4929. PUL_INTERNAL_REQUEST pRequest;
  4930. //
  4931. // A Lot of sanity checks.
  4932. //
  4933. PAGED_CODE();
  4934. ASSERT(pTracker);
  4935. ASSERT(IS_VALID_FULL_TRACKER(pTracker));
  4936. ASSERT(pTracker->pLogData);
  4937. Status = STATUS_SUCCESS;
  4938. pLogData = pTracker->pLogData;
  4939. pLogData->BytesTransferred = pTracker->IoStatus.Information;
  4940. pRequest = pLogData->pRequest;
  4941. ASSERT(UL_IS_VALID_INTERNAL_REQUEST(pRequest));
  4942. UlTrace(LOGGING,("Ul!UlLogHttpCacheHit: pLogData %p\n",pTracker->pLogData));
  4943. //
  4944. // Restore the logging data from cache. If this hasn't been done by
  4945. // the BuildCacheEntry for cache&send responses.
  4946. //
  4947. if ( pLogData->CacheAndSendResponse == FALSE )
  4948. {
  4949. ASSERT(pTracker->pUriEntry);
  4950. // Make sure that internal buffer is big enough
  4951. // before proceeding.
  4952. // pLogData->Length = pTracker->pUriEntry->MaxLength;
  4953. switch( pLogData->Format )
  4954. {
  4955. case HttpLoggingTypeW3C:
  4956. {
  4957. // Recalculate the log line size to see if need to realloc
  4958. NewLength = UlpRecalcLogLineLengthW3C(
  4959. pLogData->Flags,
  4960. pLogData->pRequest,
  4961. pTracker->pUriEntry->LogDataLength
  4962. );
  4963. if ( NewLength > UL_LOG_LINE_BUFFER_SIZE )
  4964. {
  4965. Status = UlpReallocLogLine(pLogData, NewLength);
  4966. if (!NT_SUCCESS(Status))
  4967. {
  4968. // Worker won't get called. Cleanup immediately
  4969. UlDestroyLogDataBuffer(pLogData);
  4970. return Status;
  4971. }
  4972. }
  4973. if ( pTracker->pUriEntry->LogDataLength )
  4974. {
  4975. RtlCopyMemory( &pLogData->Line[pTracker->pUriEntry->UsedOffset1],
  4976. pTracker->pUriEntry->pLogData,
  4977. pTracker->pUriEntry->LogDataLength
  4978. );
  4979. }
  4980. // Cache data plus the reserved space for date and time
  4981. pLogData->Used = pTracker->pUriEntry->LogDataLength +
  4982. pTracker->pUriEntry->UsedOffset1;
  4983. }
  4984. break;
  4985. case HttpLoggingTypeNCSA:
  4986. {
  4987. PCHAR psz,pBuffer;
  4988. ASSERT( pTracker->pUriEntry->LogDataLength );
  4989. ASSERT( pTracker->pUriEntry->pLogData );
  4990. NewLength = MAX_NCSA_CACHE_FIELD_OVERHEAD
  4991. + pTracker->pUriEntry->LogDataLength;
  4992. if ( NewLength > UL_LOG_LINE_BUFFER_SIZE )
  4993. {
  4994. Status = UlpReallocLogLine(pLogData, NewLength);
  4995. if (!NT_SUCCESS(Status))
  4996. {
  4997. // Worker won't get called. Cleanup immediately
  4998. UlDestroyLogDataBuffer(pLogData);
  4999. return Status;
  5000. }
  5001. }
  5002. psz = pBuffer = &pLogData->Line[0];
  5003. // Client IP
  5004. psz = UlStrPrintIP(
  5005. psz,
  5006. pRequest->pHttpConn->pConnection->RemoteAddress,
  5007. ' '
  5008. );
  5009. // Fixed dash
  5010. *psz++ = '-'; *psz++ = ' ';
  5011. // Authenticated users cannot be served from cache.
  5012. *psz++ = '-'; *psz++ = ' ';
  5013. // Mark the beginning of the date & time fields
  5014. pLogData->UsedOffset1 = (USHORT) DIFF(psz - pBuffer);
  5015. // Forward psz to bypass the reserved space
  5016. psz += NCSA_FIX_DATE_AND_TIME_FIELD_SIZE;
  5017. RtlCopyMemory( psz,
  5018. pTracker->pUriEntry->pLogData,
  5019. pTracker->pUriEntry->LogDataLength
  5020. );
  5021. psz += pTracker->pUriEntry->LogDataLength;
  5022. // Protocol Version
  5023. psz = UlStrPrintStr(
  5024. psz,
  5025. UL_GET_NAME_FOR_HTTP_VERSION(pRequest->Version),
  5026. '\"'
  5027. );
  5028. *psz++ = ' ';
  5029. // ProtocolStatus
  5030. psz = UlStrPrintProtocolStatus(
  5031. psz,
  5032. pTracker->pUriEntry->StatusCode,
  5033. ' '
  5034. );
  5035. // Calculate the length until now
  5036. pLogData->Used += (ULONG) DIFF(psz - pBuffer);
  5037. }
  5038. break;
  5039. case HttpLoggingTypeIIS:
  5040. {
  5041. PCHAR psz;
  5042. ULONG BytesWritten;
  5043. LONGLONG LifeTime;
  5044. LARGE_INTEGER CurrentTimeStamp;
  5045. ASSERT( pTracker->pUriEntry->LogDataLength );
  5046. ASSERT( pTracker->pUriEntry->pLogData );
  5047. NewLength = MAX_IIS_CACHE_FIELD_OVERHEAD
  5048. + pTracker->pUriEntry->LogDataLength;
  5049. if ( NewLength > UL_LOG_LINE_BUFFER_SIZE )
  5050. {
  5051. Status = UlpReallocLogLine(pLogData, NewLength);
  5052. if (!NT_SUCCESS(Status))
  5053. {
  5054. // Worker won't get called. Cleanup immediately
  5055. UlDestroyLogDataBuffer(pLogData);
  5056. return Status;
  5057. }
  5058. }
  5059. BytesWritten = 0;
  5060. // Followings specify the size of iis log line fragments
  5061. // two and three. We are constructing the fragment one
  5062. // completely from scratch for pure cache hits.
  5063. pLogData->UsedOffset1 = pTracker->pUriEntry->UsedOffset1;
  5064. pLogData->UsedOffset2 = pTracker->pUriEntry->UsedOffset2;
  5065. ASSERT(pLogData->UsedOffset1 + pLogData->UsedOffset2
  5066. == pTracker->pUriEntry->LogDataLength );
  5067. psz = &pLogData->Line[0];
  5068. // Client IP
  5069. psz = UlStrPrintIP(
  5070. psz,
  5071. pRequest->pHttpConn->pConnection->RemoteAddress,
  5072. ','
  5073. );
  5074. *psz++ = ' ';
  5075. // Authenticated users cannot be served from cache.
  5076. *psz++ = '-'; *psz++ = ','; *psz++ = ' ';
  5077. // Date & Time fields
  5078. UlpGetDateTimeFields(
  5079. HttpLoggingTypeIIS,
  5080. psz,
  5081. &BytesWritten,
  5082. NULL,
  5083. NULL
  5084. );
  5085. psz += BytesWritten; *psz++ = ','; *psz++ = ' ';
  5086. UlpGetDateTimeFields(
  5087. HttpLoggingTypeIIS,
  5088. NULL,
  5089. NULL,
  5090. psz,
  5091. &BytesWritten
  5092. );
  5093. psz += BytesWritten; *psz++ = ','; *psz++ = ' ';
  5094. // Fragment two
  5095. RtlCopyMemory( psz,
  5096. pTracker->pUriEntry->pLogData,
  5097. pLogData->UsedOffset1
  5098. );
  5099. psz += pLogData->UsedOffset1;
  5100. KeQuerySystemTime( &CurrentTimeStamp );
  5101. LifeTime = CurrentTimeStamp.QuadPart - pRequest->TimeStamp.QuadPart;
  5102. if (LifeTime < 0)
  5103. {
  5104. LifeTime = 0;
  5105. UlTrace(LOGGING, ("CopyTimeStampField: failure.\n"));
  5106. }
  5107. LifeTime /= (10*1000); // Conversion from 100-nanosecond to millisecs.
  5108. psz = UlStrPrintUlonglong(psz, (ULONGLONG)LifeTime,',');
  5109. *psz++ = ' ';
  5110. psz = UlStrPrintUlonglong(psz, pRequest->BytesReceived,',');
  5111. *psz++ = ' ';
  5112. psz = UlStrPrintUlonglong(psz, pLogData->BytesTransferred,',');
  5113. *psz++ = ' ';
  5114. // Fragment three
  5115. RtlCopyMemory( psz,
  5116. &pTracker->pUriEntry->pLogData[pLogData->UsedOffset1],
  5117. pLogData->UsedOffset2
  5118. );
  5119. psz += pLogData->UsedOffset2;
  5120. // Calculate the full size of the final log line
  5121. pLogData->Used += (ULONG) DIFF(psz - &pLogData->Line[0]);
  5122. // Reset the UsedOffset1 & 2 to zero to tell that the log line
  5123. // is not fragmented anymore but a complete line.
  5124. pLogData->UsedOffset1 = pLogData->UsedOffset2 = 0;
  5125. }
  5126. break;
  5127. default:
  5128. ASSERT(!"Unknown Log Format.\n");
  5129. }
  5130. }
  5131. //
  5132. // For cache hits we can get the corresponding cgroup from uri_cache
  5133. // entry to avoid the costly cgroup lookup. And also not to keep the
  5134. // entry around during the logging process we will have a direct ref
  5135. // to the actual cgroup. DestroyLogBuffer will release this refcount
  5136. //
  5137. if ( pTracker->pUriEntry->ConfigInfo.pLoggingConfig != NULL )
  5138. {
  5139. REFERENCE_CONFIG_GROUP(pTracker->pUriEntry->ConfigInfo.pLoggingConfig);
  5140. pLogData->pConfigGroup = pTracker->pUriEntry->ConfigInfo.pLoggingConfig;
  5141. }
  5142. pTracker->pLogData = NULL;
  5143. //
  5144. // We cannot allow send-response to resume parsing otherwise request buffers
  5145. // will be freed up. Therefore complete inline for the time being.
  5146. //
  5147. // UL_QUEUE_WORK_ITEM( &pLogData->WorkItem, &UlLogHttpCacheHitWorker);
  5148. UlLogHttpCacheHitWorker( &pLogData->WorkItem );
  5149. return Status;
  5150. }
  5151. /***************************************************************************++
  5152. Routine Description:
  5153. UlLogHttpCacheHitWorker :
  5154. Please read the description of UlLogHttpHit first.
  5155. Here where we log the cache hits. We use cache entry itself -
  5156. acquired from tracker - . Some fields are generated at this
  5157. time; i.e. timetaken,date,time...
  5158. Arguments:
  5159. PUL_WORK_ITEM : Will point us to the right pLogData
  5160. --***************************************************************************/
  5161. VOID
  5162. UlLogHttpCacheHitWorker(
  5163. IN PUL_WORK_ITEM pWorkItem
  5164. )
  5165. {
  5166. NTSTATUS Status;
  5167. PUL_INTERNAL_REQUEST pRequest;
  5168. PUL_LOG_DATA_BUFFER pLogData;
  5169. PCHAR psz;
  5170. PCHAR pBuffer;
  5171. LONGLONG LifeTime;
  5172. LARGE_INTEGER CurrentTimeStamp;
  5173. ULONG BytesWritten;
  5174. PUL_LOG_FILE_ENTRY pEntry;
  5175. ULONG Flags;
  5176. //
  5177. // A Lot of sanity checks.
  5178. //
  5179. PAGED_CODE();
  5180. pLogData = CONTAINING_RECORD(
  5181. pWorkItem,
  5182. UL_LOG_DATA_BUFFER,
  5183. WorkItem
  5184. );
  5185. ASSERT(pLogData);
  5186. UlTrace(LOGGING,("Ul!UlLogHttpCacheHitWorker: pLogData %p\n", pLogData));
  5187. pRequest = pLogData->pRequest;
  5188. ASSERT(UL_IS_VALID_INTERNAL_REQUEST(pRequest));
  5189. //
  5190. // Unlike the LogHttpHit function for non-cache hits we do not require
  5191. // the pResponse here. Tracker & pRequest already provides the info
  5192. // about how much data we sent & the time taken.
  5193. //
  5194. if (pLogData->pConfigGroup == NULL ||
  5195. IS_LOGGING_DISABLED(pLogData->pConfigGroup))
  5196. {
  5197. ASSERT(pLogData->pConfigGroup != NULL);
  5198. UlTrace( LOGGING,
  5199. ("Http!UlLogHttpCacheHitWorker: Skipping pLogData->pConfigGroup %p\n",
  5200. pLogData->pConfigGroup
  5201. ));
  5202. //
  5203. // If logging is disabled or log settings don't exist then do not
  5204. // proceed. Just exit out. But not before cleanup still goto end.
  5205. //
  5206. Status = STATUS_SUCCESS;
  5207. goto end;
  5208. }
  5209. ASSERT(IS_VALID_CONFIG_GROUP(pLogData->pConfigGroup));
  5210. //
  5211. // Construct the remaining fields before logging out this hit.
  5212. //
  5213. switch(pLogData->Format)
  5214. {
  5215. case HttpLoggingTypeW3C:
  5216. {
  5217. Flags = pLogData->Flags;
  5218. // First write the date & time fields to the beginning reserved
  5219. // space. Do not increment the used counter for date & time, b/c
  5220. // CaptureLogFields already did this when reserving the space.
  5221. psz = &pLogData->Line[0];
  5222. if ( Flags & MD_EXTLOG_DATE )
  5223. {
  5224. BytesWritten = 0;
  5225. UlpGetDateTimeFields(
  5226. HttpLoggingTypeW3C,
  5227. psz,
  5228. &BytesWritten,
  5229. NULL,
  5230. NULL
  5231. );
  5232. psz += BytesWritten; *psz++ = ' ';
  5233. ASSERT(BytesWritten == 10);
  5234. }
  5235. if ( Flags & MD_EXTLOG_TIME )
  5236. {
  5237. BytesWritten = 0;
  5238. UlpGetDateTimeFields(
  5239. HttpLoggingTypeW3C,
  5240. NULL,
  5241. NULL,
  5242. psz,
  5243. &BytesWritten
  5244. );
  5245. psz += BytesWritten; *psz++ = ' ';
  5246. ASSERT(BytesWritten == 8);
  5247. }
  5248. pBuffer = psz = &pLogData->Line[pLogData->Used];
  5249. // For pure cache hits we have to generate the few more fields
  5250. if ( pLogData->CacheAndSendResponse == FALSE )
  5251. {
  5252. // Capture log fields from Request alive
  5253. if ( Flags & MD_EXTLOG_USERNAME )
  5254. {
  5255. *psz++ = '-'; *psz++ = ' ';
  5256. }
  5257. if ( Flags & MD_EXTLOG_CLIENT_IP )
  5258. {
  5259. psz = UlStrPrintIP(
  5260. psz,
  5261. pRequest->pHttpConn->pConnection->RemoteAddress,
  5262. ' '
  5263. );
  5264. }
  5265. if ( Flags & MD_EXTLOG_PROTOCOL_VERSION )
  5266. {
  5267. psz = UlStrPrintStr(
  5268. psz,
  5269. UL_GET_NAME_FOR_HTTP_VERSION(pRequest->Version),
  5270. ' '
  5271. );
  5272. }
  5273. if ( Flags & MD_EXTLOG_USER_AGENT )
  5274. {
  5275. if (pRequest->HeaderValid[HttpHeaderUserAgent] &&
  5276. pRequest->Headers[HttpHeaderUserAgent].HeaderLength)
  5277. {
  5278. psz = UlStrPrintStrC(
  5279. psz,
  5280. (const CHAR *)pRequest->Headers[HttpHeaderUserAgent].pHeader,
  5281. ' '
  5282. );
  5283. }
  5284. else
  5285. {
  5286. *psz++ = '-'; *psz++ = ' ';
  5287. }
  5288. }
  5289. if ( Flags & MD_EXTLOG_COOKIE )
  5290. {
  5291. if (pRequest->HeaderValid[HttpHeaderCookie] &&
  5292. pRequest->Headers[HttpHeaderCookie].HeaderLength)
  5293. {
  5294. psz = UlStrPrintStrC(
  5295. psz,
  5296. (const CHAR *)pRequest->Headers[HttpHeaderCookie].pHeader,
  5297. ' '
  5298. );
  5299. }
  5300. else
  5301. {
  5302. *psz++ = '-'; *psz++ = ' ';
  5303. }
  5304. }
  5305. if ( Flags & MD_EXTLOG_REFERER )
  5306. {
  5307. if (pRequest->HeaderValid[HttpHeaderReferer] &&
  5308. pRequest->Headers[HttpHeaderReferer].HeaderLength)
  5309. {
  5310. psz = UlStrPrintStrC(
  5311. psz,
  5312. (const CHAR *)pRequest->Headers[HttpHeaderReferer].pHeader,
  5313. ' '
  5314. );
  5315. }
  5316. else
  5317. {
  5318. *psz++ = '-'; *psz++ = ' ';
  5319. }
  5320. }
  5321. if ( Flags & MD_EXTLOG_HOST )
  5322. {
  5323. if (pRequest->HeaderValid[HttpHeaderHost] &&
  5324. pRequest->Headers[HttpHeaderHost].HeaderLength)
  5325. {
  5326. psz = UlStrPrintStrC(
  5327. psz,
  5328. (const CHAR *)pRequest->Headers[HttpHeaderHost].pHeader,
  5329. ' '
  5330. );
  5331. }
  5332. else
  5333. {
  5334. *psz++ = '-'; *psz++ = ' ';
  5335. }
  5336. }
  5337. }
  5338. // Now proceed with the remaining fields, but add them to the end.
  5339. if ( pLogData->Flags & MD_EXTLOG_BYTES_SENT )
  5340. {
  5341. psz = UlStrPrintUlonglong(psz, pLogData->BytesTransferred,' ');
  5342. }
  5343. if ( pLogData->Flags & MD_EXTLOG_BYTES_RECV )
  5344. {
  5345. psz = UlStrPrintUlonglong(psz, pRequest->BytesReceived,' ');
  5346. }
  5347. if ( pLogData->Flags & MD_EXTLOG_TIME_TAKEN )
  5348. {
  5349. KeQuerySystemTime( &CurrentTimeStamp );
  5350. LifeTime = CurrentTimeStamp.QuadPart - pRequest->TimeStamp.QuadPart;
  5351. if (LifeTime < 0)
  5352. {
  5353. LifeTime = 0;
  5354. UlTrace( LOGGING, ("CopyTimeStampField failure.\n"));
  5355. }
  5356. LifeTime /= (10*1000); // Conversion from 100-nanosecond to millisecs.
  5357. psz = UlStrPrintUlonglong(psz, (ULONGLONG)LifeTime,' ');
  5358. }
  5359. // Calculate the used space
  5360. pLogData->Used += (ULONG) DIFF(psz - pBuffer);
  5361. // Eat the last space and write the \r\n to the end.
  5362. // Only if we have any fields picked and written
  5363. if ( pLogData->Used )
  5364. {
  5365. psz = &pLogData->Line[pLogData->Used-1]; // Eat the last space
  5366. *psz++ = '\r'; *psz++ = '\n'; *psz++ = ANSI_NULL;
  5367. pLogData->Used += 1;
  5368. if ( pLogData->Length == 0 )
  5369. {
  5370. ASSERT( pLogData->Used <= UL_LOG_LINE_BUFFER_SIZE );
  5371. }
  5372. else
  5373. {
  5374. ASSERT( pLogData->Used <= pLogData->Length );
  5375. }
  5376. }
  5377. else
  5378. {
  5379. goto end; // No log fields nothing to log
  5380. }
  5381. // Cleanup the UsedOffsets
  5382. pLogData->UsedOffset1 = pLogData->UsedOffset2 = 0;
  5383. }
  5384. break;
  5385. case HttpLoggingTypeNCSA:
  5386. {
  5387. // [date:time GmtOffset] -> "[07/Jan/2000:00:02:23 -0800] "
  5388. // Restore the pointer to the reserved space first.
  5389. psz = &pLogData->Line[pLogData->UsedOffset1];
  5390. *psz++ = '[';
  5391. BytesWritten = 0;
  5392. UlpGetDateTimeFields(
  5393. HttpLoggingTypeNCSA,
  5394. psz,
  5395. &BytesWritten,
  5396. NULL,
  5397. NULL
  5398. );
  5399. psz += BytesWritten; *psz++ = ':';
  5400. ASSERT(BytesWritten == 11);
  5401. BytesWritten = 0;
  5402. UlpGetDateTimeFields(
  5403. HttpLoggingTypeNCSA,
  5404. NULL,
  5405. NULL,
  5406. psz,
  5407. &BytesWritten
  5408. );
  5409. psz += BytesWritten; *psz++ = ' ';
  5410. ASSERT(BytesWritten == 8);
  5411. UlAcquireResourceShared(&g_pUlNonpagedData->LogListResource, TRUE);
  5412. psz = UlStrPrintStr(psz, g_GMTOffset,']');
  5413. UlReleaseResource(&g_pUlNonpagedData->LogListResource);
  5414. *psz++ = ' ';
  5415. ASSERT(((ULONG) DIFF(psz - &pLogData->Line[pLogData->UsedOffset1]) == 29));
  5416. // BytesSent
  5417. pBuffer = psz = &pLogData->Line[pLogData->Used];
  5418. psz = UlStrPrintUlonglong(psz, pLogData->BytesTransferred,'\r');
  5419. pLogData->Used += (ULONG) DIFF(psz - pBuffer);
  5420. // \n\0
  5421. *psz++ = '\n'; *psz++ = ANSI_NULL;
  5422. pLogData->Used += 1;
  5423. // Cleanup the UsedOffsets
  5424. pLogData->UsedOffset1 = pLogData->UsedOffset2 = 0;
  5425. }
  5426. break;
  5427. case HttpLoggingTypeIIS:
  5428. {
  5429. if ( pLogData->CacheAndSendResponse == TRUE )
  5430. {
  5431. // We need to work on the fragmented log line
  5432. // which is coming from the originaly allocated
  5433. // line but not from cache.
  5434. // Complete Fragment 1
  5435. pBuffer = psz = &pLogData->Line[pLogData->UsedOffset1];
  5436. UlpGetDateTimeFields(
  5437. HttpLoggingTypeIIS,
  5438. psz,
  5439. &BytesWritten,
  5440. NULL,
  5441. NULL
  5442. );
  5443. psz += BytesWritten; *psz++ = ','; *psz++ = ' ';
  5444. UlpGetDateTimeFields(
  5445. HttpLoggingTypeIIS,
  5446. NULL,
  5447. NULL,
  5448. psz,
  5449. &BytesWritten
  5450. );
  5451. psz += BytesWritten; *psz++ = ','; *psz++ = ' ';
  5452. pLogData->UsedOffset1 += (USHORT) DIFF(psz - pBuffer);
  5453. // Complete Fragment 2
  5454. pBuffer = psz = &pLogData->Line[512 + pLogData->UsedOffset2];
  5455. KeQuerySystemTime( &CurrentTimeStamp );
  5456. LifeTime = CurrentTimeStamp.QuadPart - pRequest->TimeStamp.QuadPart;
  5457. if (LifeTime < 0)
  5458. {
  5459. LifeTime = 0;
  5460. UlTrace(LOGGING, ("CopyTimeStampField: failure.\n"));
  5461. }
  5462. LifeTime /= (10*1000); // Conversion from 100-nanosecond to millisecs.
  5463. psz = UlStrPrintUlonglong(psz, (ULONGLONG)LifeTime,',');
  5464. *psz++ = ' ';
  5465. psz = UlStrPrintUlonglong(psz, pRequest->BytesReceived,',');
  5466. *psz++ = ' ';
  5467. psz = UlStrPrintUlonglong(psz, pLogData->BytesTransferred,',');
  5468. *psz++ = ' ';
  5469. pLogData->UsedOffset2 += (USHORT) DIFF(psz - pBuffer);
  5470. // Size of the final log line is
  5471. // pLogData->UsedOffset1 + pLogData->UsedOffset2 + pLogData->Used
  5472. }
  5473. // Or else IIS log line is already done. We have completed it before
  5474. // relasing the cache entry.
  5475. }
  5476. break;
  5477. default:
  5478. {
  5479. ASSERT(!"Unknown Log Format Type\n");
  5480. Status = STATUS_INVALID_PARAMETER;
  5481. goto end;
  5482. }
  5483. }
  5484. //
  5485. // Finally this log line is ready to rock. Lets write it out.
  5486. //
  5487. UlAcquireResourceShared(&g_pUlNonpagedData->LogListResource, TRUE);
  5488. pEntry = pLogData->pConfigGroup->pLogFileEntry;
  5489. if (pEntry)
  5490. {
  5491. ASSERT(IS_VALID_LOG_FILE_ENTRY(pEntry));
  5492. Status =
  5493. UlpWriteToLogFile(
  5494. pEntry,
  5495. pLogData->UsedOffset1 + pLogData->UsedOffset2 + pLogData->Used,
  5496. &pLogData->Line[0],
  5497. pLogData->UsedOffset1,
  5498. pLogData->UsedOffset2
  5499. );
  5500. }
  5501. UlReleaseResource(&g_pUlNonpagedData->LogListResource);
  5502. end:
  5503. if (pLogData)
  5504. {
  5505. //
  5506. // Cleanup the references & the log buffer
  5507. //
  5508. UlDestroyLogDataBuffer(pLogData);
  5509. }
  5510. #if DBG
  5511. if (!NT_SUCCESS(Status))
  5512. {
  5513. UlTrace(LOGGING,("Http!UlLogHttpcacheHitWorker: failure 0x%08lx \n",
  5514. Status
  5515. ));
  5516. }
  5517. #endif
  5518. return;
  5519. }
  5520. /***************************************************************************++
  5521. Routine Description:
  5522. Initializes the Log Date & Time Cache
  5523. --***************************************************************************/
  5524. VOID
  5525. UlpInitializeLogCache(
  5526. VOID
  5527. )
  5528. {
  5529. LARGE_INTEGER SystemTime;
  5530. ULONG LogType;
  5531. ExInitializeFastMutex( &g_LogCacheFastMutex);
  5532. KeQuerySystemTime(&SystemTime);
  5533. for ( LogType=0; LogType<HttpLoggingTypeMaximum; LogType++ )
  5534. {
  5535. UlpGenerateDateAndTimeFields( (HTTP_LOGGING_TYPE) LogType,
  5536. SystemTime,
  5537. g_UlDateTimeCache[LogType].Date,
  5538. &g_UlDateTimeCache[LogType].DateLength,
  5539. g_UlDateTimeCache[LogType].Time,
  5540. &g_UlDateTimeCache[LogType].TimeLength
  5541. );
  5542. g_UlDateTimeCache[LogType].LastSystemTime.QuadPart = SystemTime.QuadPart;
  5543. }
  5544. }
  5545. /***************************************************************************++
  5546. Routine Description:
  5547. Generates all possible types of date/time fields from a LARGE_INTEGER.
  5548. Arguments:
  5549. CurrentTime: A 64 bit Time value to be converted.
  5550. --***************************************************************************/
  5551. VOID
  5552. UlpGenerateDateAndTimeFields(
  5553. IN HTTP_LOGGING_TYPE LogType,
  5554. IN LARGE_INTEGER CurrentTime,
  5555. OUT PCHAR pDate,
  5556. OUT PULONG pDateLength,
  5557. OUT PCHAR pTime,
  5558. OUT PULONG pTimeLength
  5559. )
  5560. {
  5561. TIME_FIELDS CurrentTimeFields;
  5562. LARGE_INTEGER CurrentTimeLoc;
  5563. TIME_FIELDS CurrentTimeFieldsLoc;
  5564. PCHAR psz;
  5565. LONG Length;
  5566. // This routine does touch to pageable memory if the default log buffer
  5567. // wasn't sufficent enough to hold log fields and get reallocated from
  5568. // paged pool. For this reason the date&time cache can not use SpinLocks.
  5569. PAGED_CODE();
  5570. ASSERT(LogType < HttpLoggingTypeMaximum);
  5571. RtlTimeToTimeFields( &CurrentTime, &CurrentTimeFields );
  5572. switch(LogType)
  5573. {
  5574. case HttpLoggingTypeW3C:
  5575. //
  5576. // Uses GMT with format as follows;
  5577. //
  5578. // 2000-01-31 00:12:23
  5579. //
  5580. if (pDate)
  5581. {
  5582. psz = pDate;
  5583. psz = UlStrPrintUlongPad(psz, CurrentTimeFields.Year, 4, '-' );
  5584. psz = UlStrPrintUlongPad(psz, CurrentTimeFields.Month,2, '-' );
  5585. psz = UlStrPrintUlongPad(psz, CurrentTimeFields.Day, 2, '\0');
  5586. *pDateLength = (ULONG) DIFF(psz - pDate);
  5587. }
  5588. if (pTime)
  5589. {
  5590. psz = pTime;
  5591. psz = UlStrPrintUlongPad(psz, CurrentTimeFields.Hour, 2, ':' );
  5592. psz = UlStrPrintUlongPad(psz, CurrentTimeFields.Minute,2, ':' );
  5593. psz = UlStrPrintUlongPad(psz, CurrentTimeFields.Second,2, '\0');
  5594. *pTimeLength = (ULONG) DIFF(psz - pTime);
  5595. }
  5596. break;
  5597. case HttpLoggingTypeNCSA:
  5598. //
  5599. // Uses GMT Time with format as follows;
  5600. //
  5601. // 07/Jan/2000 00:02:23
  5602. //
  5603. ExSystemTimeToLocalTime( &CurrentTime, &CurrentTimeLoc );
  5604. RtlTimeToTimeFields( &CurrentTimeLoc, &CurrentTimeFieldsLoc );
  5605. if(pDate)
  5606. {
  5607. psz = pDate;
  5608. psz = UlStrPrintUlongPad(psz, CurrentTimeFieldsLoc.Day, 2, '/' );
  5609. psz = UlStrPrintStr(psz, UL_GET_MONTH_AS_STR(CurrentTimeFieldsLoc.Month),'/');
  5610. psz = UlStrPrintUlongPad(psz, CurrentTimeFieldsLoc.Year,4, '\0');
  5611. *pDateLength = (ULONG) DIFF(psz - pDate);
  5612. }
  5613. if(pTime)
  5614. {
  5615. psz = pTime;
  5616. psz = UlStrPrintUlongPad(psz, CurrentTimeFieldsLoc.Hour, 2, ':' );
  5617. psz = UlStrPrintUlongPad(psz, CurrentTimeFieldsLoc.Minute,2, ':' );
  5618. psz = UlStrPrintUlongPad(psz, CurrentTimeFieldsLoc.Second,2, '\0');
  5619. *pTimeLength = (ULONG) DIFF(psz - pTime);
  5620. }
  5621. break;
  5622. case HttpLoggingTypeIIS:
  5623. //
  5624. // Uses LOCAL Time with format as follows;
  5625. // This should be localised if we can solve the problem.
  5626. //
  5627. // 1/31/2000 0:02:03
  5628. //
  5629. ExSystemTimeToLocalTime( &CurrentTime, &CurrentTimeLoc );
  5630. RtlTimeToTimeFields( &CurrentTimeLoc, &CurrentTimeFieldsLoc );
  5631. if (pDate)
  5632. {
  5633. psz = pDate;
  5634. psz = UlStrPrintUlongPad(psz, CurrentTimeFieldsLoc.Month, 0, '/' );
  5635. psz = UlStrPrintUlongPad(psz, CurrentTimeFieldsLoc.Day, 0, '/' );
  5636. psz = UlStrPrintUlongPad(psz, CurrentTimeFieldsLoc.Year, 0, '\0');
  5637. *pDateLength = (ULONG) DIFF(psz - pDate);
  5638. }
  5639. if(pTime)
  5640. {
  5641. psz = pTime;
  5642. psz = UlStrPrintUlongPad(psz, CurrentTimeFieldsLoc.Hour, 0, ':' );
  5643. psz = UlStrPrintUlongPad(psz, CurrentTimeFieldsLoc.Minute,2, ':' );
  5644. psz = UlStrPrintUlongPad(psz, CurrentTimeFieldsLoc.Second,2, '\0');
  5645. *pTimeLength = (ULONG) DIFF(psz - pTime);
  5646. }
  5647. break;
  5648. }
  5649. return;
  5650. }
  5651. /***************************************************************************++
  5652. Routine Description:
  5653. Generates a date header and updates cached value if required.
  5654. Caller should overwrite the terminating null by a space or comma.
  5655. Arguments:
  5656. Date and Time are optional. But one of them should be provided.
  5657. --***************************************************************************/
  5658. VOID
  5659. UlpGetDateTimeFields(
  5660. IN HTTP_LOGGING_TYPE LogType,
  5661. OUT PCHAR pDate,
  5662. OUT PULONG pDateLength,
  5663. OUT PCHAR pTime,
  5664. OUT PULONG pTimeLength
  5665. )
  5666. {
  5667. LARGE_INTEGER SystemTime;
  5668. LARGE_INTEGER CacheTime;
  5669. LONG Length;
  5670. LONGLONG Timediff;
  5671. PAGED_CODE();
  5672. ASSERT(LogType < HttpLoggingTypeMaximum);
  5673. ASSERT(pDate || pTime);
  5674. //
  5675. // Get the current time.
  5676. //
  5677. KeQuerySystemTime( &SystemTime );
  5678. CacheTime.QuadPart =
  5679. g_UlDateTimeCache[LogType].LastSystemTime.QuadPart;
  5680. //
  5681. // Check the difference between the current time, and
  5682. // the cached time.
  5683. //
  5684. Timediff = SystemTime.QuadPart - CacheTime.QuadPart;
  5685. if (Timediff < ONE_SECOND)
  5686. {
  5687. //
  5688. // The cached date&time hasn't gone stale yet.We can copy.
  5689. // Force a barrier around reading the string into memory.
  5690. //
  5691. UL_READMOSTLY_READ_BARRIER();
  5692. if (pDate)
  5693. {
  5694. RtlCopyMemory( pDate,
  5695. g_UlDateTimeCache[LogType].Date,
  5696. g_UlDateTimeCache[LogType].DateLength
  5697. );
  5698. *pDateLength = g_UlDateTimeCache[LogType].DateLength;
  5699. }
  5700. if (pTime)
  5701. {
  5702. RtlCopyMemory( pTime,
  5703. g_UlDateTimeCache[LogType].Time,
  5704. g_UlDateTimeCache[LogType].TimeLength
  5705. );
  5706. *pTimeLength = g_UlDateTimeCache[LogType].TimeLength;
  5707. }
  5708. UL_READMOSTLY_READ_BARRIER();
  5709. //
  5710. // Get grab the cached time value again in case it has been changed.
  5711. // As you notice we do not have a lock around this part of the code.
  5712. //
  5713. if (CacheTime.QuadPart ==
  5714. g_UlDateTimeCache[LogType].LastSystemTime.QuadPart)
  5715. {
  5716. //
  5717. // Value hasn't changed. We are all set.
  5718. //
  5719. return;
  5720. }
  5721. //
  5722. // Otherwise fall down and flush the cache, and then recopy.
  5723. //
  5724. }
  5725. //
  5726. // The cached date & time is stale. We need to update it.
  5727. //
  5728. ExAcquireFastMutex( &g_LogCacheFastMutex );
  5729. //
  5730. // Has someone else updated the time while we were blocked?
  5731. //
  5732. CacheTime.QuadPart =
  5733. g_UlDateTimeCache[LogType].LastSystemTime.QuadPart;
  5734. Timediff = SystemTime.QuadPart - CacheTime.QuadPart;
  5735. if (Timediff >= ONE_SECOND)
  5736. {
  5737. g_UlDateTimeCache[LogType].LastSystemTime.QuadPart = 0;
  5738. KeQuerySystemTime( &SystemTime );
  5739. UL_READMOSTLY_WRITE_BARRIER();
  5740. UlpGenerateDateAndTimeFields(
  5741. LogType,
  5742. SystemTime,
  5743. g_UlDateTimeCache[LogType].Date,
  5744. &g_UlDateTimeCache[LogType].DateLength,
  5745. g_UlDateTimeCache[LogType].Time,
  5746. &g_UlDateTimeCache[LogType].TimeLength
  5747. );
  5748. UL_READMOSTLY_WRITE_BARRIER();
  5749. g_UlDateTimeCache[LogType].LastSystemTime.QuadPart =
  5750. SystemTime.QuadPart;
  5751. }
  5752. //
  5753. // The time has been updated. Copy the new string into
  5754. // the caller's buffer.
  5755. //
  5756. if (pDate)
  5757. {
  5758. RtlCopyMemory( pDate,
  5759. g_UlDateTimeCache[LogType].Date,
  5760. g_UlDateTimeCache[LogType].DateLength
  5761. );
  5762. *pDateLength = g_UlDateTimeCache[LogType].DateLength;
  5763. }
  5764. if (pTime)
  5765. {
  5766. RtlCopyMemory( pTime,
  5767. g_UlDateTimeCache[LogType].Time,
  5768. g_UlDateTimeCache[LogType].TimeLength
  5769. );
  5770. *pTimeLength = g_UlDateTimeCache[LogType].TimeLength;
  5771. }
  5772. ExReleaseFastMutex( &g_LogCacheFastMutex );
  5773. return;
  5774. }