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.

9133 lines
230 KiB

  1. /*++
  2. Copyright (c) 1999 Microsoft Corporation
  3. Module Name:
  4. prefetch.c
  5. Abstract:
  6. This module contains the prefetcher for optimizing demand
  7. paging. Page faults for a scenario are logged and the next time
  8. scenario starts, these pages are prefetched efficiently via
  9. asynchronous paging I/O.
  10. Author:
  11. Arthur Zwiegincew (arthurz) 13-May-1999
  12. Stuart Sechrest (stuartse) 15-Jul-1999
  13. Chuck Lenzmeier (chuckl) 15-Mar-2000
  14. Cenk Ergan (cenke) 15-Mar-2000
  15. Revision History:
  16. --*/
  17. #include "cc.h"
  18. #include "zwapi.h"
  19. #include "prefetch.h"
  20. #include "preftchp.h"
  21. #include "stdio.h"
  22. #include "stdlib.h"
  23. //
  24. // Mark pagable routines to save footprint.
  25. //
  26. #ifdef ALLOC_PRAGMA
  27. #pragma alloc_text(INIT, CcPfInitializePrefetcher)
  28. #pragma alloc_text(PAGE, CcPfBeginAppLaunch)
  29. #pragma alloc_text(PAGE, CcPfBeginTrace)
  30. #pragma alloc_text(PAGE, CcPfGetPrefetchInstructions)
  31. #pragma alloc_text(PAGE, CcPfQueryScenarioInformation)
  32. #pragma alloc_text(PAGE, CcPfPrefetchFileMetadata)
  33. #pragma alloc_text(PAGE, CcPfPrefetchDirectoryContents)
  34. #pragma alloc_text(PAGE, CcPfPrefetchMetadata)
  35. #pragma alloc_text(PAGE, CcPfPrefetchScenario)
  36. #pragma alloc_text(PAGE, CcPfPrefetchSections)
  37. #pragma alloc_text(PAGE, CcPfOpenVolumesForPrefetch)
  38. #pragma alloc_text(PAGE, CcPfUpdateVolumeList)
  39. #pragma alloc_text(PAGE, CcPfFindString)
  40. #pragma alloc_text(PAGE, CcPfIsVolumeMounted)
  41. #pragma alloc_text(PAGE, CcPfQueryVolumeInfo)
  42. #pragma alloc_text(PAGE, CcPfEndTrace)
  43. #pragma alloc_text(PAGE, CcPfBuildDumpFromTrace)
  44. #pragma alloc_text(PAGE, CcPfCleanupTrace)
  45. #pragma alloc_text(PAGE, CcPfInitializePrefetchHeader)
  46. #pragma alloc_text(PAGE, CcPfCleanupPrefetchHeader)
  47. #pragma alloc_text(PAGE, CcPfEndTraceWorkerThreadRoutine)
  48. #pragma alloc_text(PAGE, CcPfInitializeRefCount)
  49. #pragma alloc_text(PAGE, CcPfAcquireExclusiveRef)
  50. #pragma alloc_text(PAGE, CcPfGetSectionObject)
  51. #pragma alloc_text(PAGE, CcPfScanCommandLine)
  52. #pragma alloc_text(PAGE, CcPfGetCompletedTrace)
  53. #pragma alloc_text(PAGE, CcPfGetFileNamesWorkerRoutine)
  54. #pragma alloc_text(PAGE, CcPfSetPrefetcherInformation)
  55. #pragma alloc_text(PAGE, CcPfQueryPrefetcherInformation)
  56. #pragma alloc_text(PAGE, CcPfProcessExitNotification)
  57. #pragma alloc_text(PAGE, PfWithinBounds)
  58. #pragma alloc_text(PAGE, PfVerifyScenarioId)
  59. #pragma alloc_text(PAGE, PfVerifyScenarioBuffer)
  60. #pragma alloc_text(PAGE, PfVerifyTraceBuffer)
  61. #endif
  62. //
  63. // Globals:
  64. //
  65. //
  66. // Whether prefetching is enabled.
  67. //
  68. LOGICAL CcPfEnablePrefetcher = 0;
  69. //
  70. // Number of active prefetcher traces.
  71. //
  72. LONG CcPfNumActiveTraces = 0;
  73. //
  74. // This structure contains prefetcher globals except the ones above
  75. // that are accessed by other kernel components. It is important that
  76. // this structure is initialized to zeros.
  77. //
  78. CCPF_PREFETCHER_GLOBALS CcPfGlobals = {0};
  79. //
  80. // Routines exported to other kernel components:
  81. //
  82. NTSTATUS
  83. CcPfInitializePrefetcher(
  84. VOID
  85. )
  86. /*++
  87. Routine Description:
  88. This routine is called to initialize the prefetcher.
  89. Arguments:
  90. None.
  91. Return Value:
  92. Status.
  93. Environment:
  94. Kernel mode. IRQL == PASSIVE_LEVEL.
  95. Notes:
  96. The code & local constants for this function gets discarded after system boots.
  97. --*/
  98. {
  99. DBGPR((CCPFID,PFTRC,"CCPF: InitializePrefetcher()\n"));
  100. //
  101. // Since CcPfGlobals is zeroed in its global definition e.g. CcPfGlobals = {0};
  102. // we don't have to initialize:
  103. //
  104. // NumCompletedTraces
  105. // CompletedTracesEvent
  106. //
  107. //
  108. // Initialize the active traces list and lock.
  109. //
  110. InitializeListHead(&CcPfGlobals.ActiveTraces);
  111. KeInitializeSpinLock(&CcPfGlobals.ActiveTracesLock);
  112. //
  113. // Initialize list of saved completed prefetch traces and its lock.
  114. //
  115. InitializeListHead(&CcPfGlobals.CompletedTraces);
  116. ExInitializeFastMutex(&CcPfGlobals.CompletedTracesLock);
  117. //
  118. // Initialize prefetcher parameters.
  119. //
  120. CcPfParametersInitialize(&CcPfGlobals.Parameters);
  121. //
  122. // Determine from the global parameters if the prefetcher is
  123. // enabled and update the global enable status.
  124. //
  125. CcPfDetermineEnablePrefetcher();
  126. //
  127. // Fall through with status.
  128. //
  129. return STATUS_SUCCESS;
  130. }
  131. NTSTATUS
  132. CcPfBeginAppLaunch(
  133. PEPROCESS Process,
  134. PVOID Section
  135. )
  136. /*++
  137. Routine Description:
  138. This routine is called when the first user thread is starting up
  139. in the process. It may attempt to access the PEB for the command
  140. line parameters.
  141. Arguments:
  142. Process - Pointer to new process created for the application.
  143. Section - Pointer to section mapped to newly created process.
  144. Return Value:
  145. Status.
  146. Environment:
  147. Kernel mode. IRQL == PASSIVE_LEVEL.
  148. --*/
  149. {
  150. LARGE_INTEGER CurrentTime;
  151. LARGE_INTEGER TimeSinceLastLaunch;
  152. PPF_SCENARIO_HEADER Scenario;
  153. NTSTATUS Status;
  154. PF_SCENARIO_ID ScenarioId;
  155. ULONG NameNumChars;
  156. ULONG PathNumChars;
  157. WCHAR *CurCharPtr;
  158. WCHAR *FileNamePtr;
  159. PULONG CommandLineHashId;
  160. ULONG NumCharsToCopy;
  161. ULONG CharIdx;
  162. STRING AnsiFilePath;
  163. UNICODE_STRING FilePath;
  164. ULONG HashId;
  165. ULONG PrefetchHint;
  166. BOOLEAN AllocatedUnicodePath;
  167. BOOLEAN ShouldTraceScenario;
  168. BOOLEAN IsHostingApplication;
  169. DBGPR((CCPFID,PFTRC,"CCPF: BeginAppLaunch()\n"));
  170. //
  171. // Initialize locals.
  172. //
  173. AllocatedUnicodePath = FALSE;
  174. Scenario = NULL;
  175. //
  176. // Check to see if the prefetcher is enabled.
  177. //
  178. if (!CCPF_IS_PREFETCHER_ENABLED()) {
  179. Status = STATUS_NOT_SUPPORTED;
  180. goto cleanup;
  181. }
  182. //
  183. // Check if prefetching is enabled for application launches.
  184. //
  185. if (CcPfGlobals.Parameters.Parameters.EnableStatus[PfApplicationLaunchScenarioType] != PfSvEnabled) {
  186. Status = STATUS_NOT_SUPPORTED;
  187. goto cleanup;
  188. }
  189. //
  190. // Don't prefetch or start tracing if there is an active system-wide trace.
  191. //
  192. if (CcPfGlobals.SystemWideTrace != NULL) {
  193. Status = STATUS_USER_EXISTS;
  194. goto cleanup;
  195. }
  196. //
  197. // Query name from the section. Unfortunately this returns us an
  198. // ANSI string which we then have to convert back to UNICODE. We
  199. // have to it this way for now because we could not add an API to
  200. // Mm.
  201. //
  202. Status = MmGetFileNameForSection(Section, &AnsiFilePath);
  203. if (!NT_SUCCESS(Status)) {
  204. goto cleanup;
  205. }
  206. //
  207. // Convert ANSI path to UNICODE path.
  208. //
  209. Status = RtlAnsiStringToUnicodeString(&FilePath, &AnsiFilePath, TRUE);
  210. //
  211. // Don't leak the ANSI buffer...
  212. //
  213. ExFreePool (AnsiFilePath.Buffer);
  214. if (!NT_SUCCESS(Status)) {
  215. goto cleanup;
  216. }
  217. AllocatedUnicodePath = TRUE;
  218. //
  219. // Scenario Id requires us to be case insensitive.
  220. //
  221. RtlUpcaseUnicodeString(&FilePath, &FilePath, FALSE);
  222. //
  223. // We need to copy just the real file name into the scenario
  224. // name. Make a first pass to calculate the size of real file
  225. // name.
  226. //
  227. NameNumChars = 0;
  228. PathNumChars = FilePath.Length / sizeof(WCHAR);
  229. for (CurCharPtr = &FilePath.Buffer[PathNumChars - 1];
  230. CurCharPtr >= FilePath.Buffer;
  231. CurCharPtr--) {
  232. if (*CurCharPtr == L'\\') {
  233. break;
  234. }
  235. NameNumChars++;
  236. }
  237. //
  238. // Check if we got a name.
  239. //
  240. if (NameNumChars == 0) {
  241. Status = STATUS_INVALID_PARAMETER;
  242. goto cleanup;
  243. }
  244. //
  245. // Set pointer to where file name begins.
  246. //
  247. FileNamePtr = &FilePath.Buffer[PathNumChars - NameNumChars];
  248. //
  249. // Copy up to PF_SCEN_ID_MAX_CHARS characters into the scenario
  250. // name buffer.
  251. //
  252. NumCharsToCopy = CCPF_MIN(PF_SCEN_ID_MAX_CHARS, NameNumChars);
  253. for (CharIdx = 0; CharIdx < NumCharsToCopy; CharIdx++) {
  254. ScenarioId.ScenName[CharIdx] = FileNamePtr[CharIdx];
  255. }
  256. //
  257. // Make sure scenario name is NUL terminated.
  258. //
  259. ScenarioId.ScenName[NumCharsToCopy] = 0;
  260. //
  261. // Calculate scenario hash id from the full path name.
  262. //
  263. ScenarioId.HashId = CcPfHashValue(FilePath.Buffer,
  264. FilePath.Length);
  265. //
  266. // If this is a "hosting" application (e.g. dllhost, rundll32, mmc)
  267. // we want to have unique scenarios based on the command line, so
  268. // we update the hash id.
  269. //
  270. IsHostingApplication = CcPfIsHostingApplication(ScenarioId.ScenName);
  271. if (IsHostingApplication) {
  272. CommandLineHashId = &HashId;
  273. } else {
  274. CommandLineHashId = NULL;
  275. }
  276. //
  277. // Scan the command line for this process, calculating a hash if
  278. // requested and checking for a prefetch hint.
  279. //
  280. Status = CcPfScanCommandLine(&PrefetchHint, CommandLineHashId);
  281. if (!NT_SUCCESS(Status)) {
  282. //
  283. // If we failed to access the PEB to get the command line,
  284. // the process may be exiting etc. Do not continue.
  285. //
  286. goto cleanup;
  287. }
  288. if (IsHostingApplication) {
  289. //
  290. // Update the hash ID calculated from full path name.
  291. //
  292. ScenarioId.HashId += HashId;
  293. }
  294. //
  295. // If there is a specific hint in the command line add it to the
  296. // hash id to make it a unique scenario.
  297. //
  298. ScenarioId.HashId += PrefetchHint;
  299. //
  300. // Get prefetch instructions for this scenario. If there are
  301. // instructions we will use them to determine whether we should
  302. // prefetch and/or trace this scenario. By default we will trace
  303. // the scenario even if there are no instructions.
  304. //
  305. ShouldTraceScenario = TRUE;
  306. Status = CcPfGetPrefetchInstructions(&ScenarioId,
  307. PfApplicationLaunchScenarioType,
  308. &Scenario);
  309. if (NT_SUCCESS(Status)) {
  310. CCPF_ASSERT(Scenario);
  311. //
  312. // Determine how much time has passed since the last launch
  313. // for which instructions were updated. Note that the way
  314. // checks are done below we will recover after a while if the
  315. // user changes the system time.
  316. //
  317. KeQuerySystemTime(&CurrentTime);
  318. TimeSinceLastLaunch.QuadPart = CurrentTime.QuadPart - Scenario->LastLaunchTime.QuadPart;
  319. if (TimeSinceLastLaunch.QuadPart >= Scenario->MinRePrefetchTime.QuadPart) {
  320. Status = CcPfPrefetchScenario(Scenario);
  321. } else {
  322. DBGPR((CCPFID,PFPREF,"CCPF: BeginAppLaunch-NotRePrefetching\n"));
  323. }
  324. if (TimeSinceLastLaunch.QuadPart < Scenario->MinReTraceTime.QuadPart) {
  325. DBGPR((CCPFID,PFPREF,"CCPF: BeginAppLaunch-NotReTracing\n"));
  326. ShouldTraceScenario = FALSE;
  327. }
  328. }
  329. if (ShouldTraceScenario) {
  330. //
  331. // Start tracing the application launch. Fall through with status.
  332. // The trace will end when we time out or when the process
  333. // terminates.
  334. //
  335. Status = CcPfBeginTrace(&ScenarioId,
  336. PfApplicationLaunchScenarioType,
  337. Process);
  338. }
  339. //
  340. // We will fall through with either the status from
  341. // CcPfGetPrefetchInstructions, CcPfPrefetchScenario or
  342. // CcPfBeginTrace.
  343. //
  344. cleanup:
  345. if (AllocatedUnicodePath) {
  346. RtlFreeUnicodeString(&FilePath);
  347. }
  348. if (Scenario) {
  349. ExFreePool(Scenario);
  350. }
  351. DBGPR((CCPFID,PFTRC,"CCPF: BeginAppLaunch()=%x\n", Status));
  352. return Status;
  353. }
  354. NTSTATUS
  355. CcPfProcessExitNotification(
  356. PEPROCESS Process
  357. )
  358. /*++
  359. Routine Description:
  360. This routine gets called when a process is exiting while there are
  361. active prefetch traces. It checks for active traces that are
  362. associated with this process, and makes sure they don't stay
  363. around much longer.
  364. Arguments:
  365. Process - Process that is terminating.
  366. Return Value:
  367. Status.
  368. Environment:
  369. Kernel mode, IRQL == PASSIVE_LEVEL.
  370. --*/
  371. {
  372. PCCPF_TRACE_HEADER Trace;
  373. DBGPR((CCPFID,PFTRC,"CCPF: ProcessExit(%p)\n", Process));
  374. //
  375. // Validate parameters. We should have been called with a valid
  376. // process.
  377. //
  378. CCPF_ASSERT(Process);
  379. //
  380. // Get the trace associated with this process if any.
  381. //
  382. Trace = CcPfReferenceProcessTrace(Process);
  383. if (Trace) {
  384. if (!InterlockedCompareExchange(&Trace->EndTraceCalled, 1, 0)) {
  385. //
  386. // We set EndTraceCalled from 0 to 1. Queue the
  387. // workitem to end the trace.
  388. //
  389. ExQueueWorkItem(&Trace->EndTraceWorkItem, DelayedWorkQueue);
  390. }
  391. CcPfDecRef(&Trace->RefCount);
  392. }
  393. //
  394. // We are done.
  395. //
  396. return STATUS_SUCCESS;
  397. }
  398. VOID
  399. CcPfLogPageFault(
  400. IN PFILE_OBJECT FileObject,
  401. IN ULONGLONG FileOffset,
  402. IN ULONG Flags
  403. )
  404. /*++
  405. Routine Description:
  406. This routine logs the specified page fault in appropriate prefetch
  407. traces.
  408. Arguments:
  409. FileObject - Supplies the file object for the faulting address.
  410. FileOffset - Supplies the file offset for the faulting address.
  411. Flags - Supplies various bits indicating attributes of the fault.
  412. Return Value:
  413. None.
  414. Environment:
  415. Kernel mode. IRQL <= DISPATCH_LEVEL.
  416. Uses interlocked slist operation.
  417. Acquires spinlock.
  418. --*/
  419. {
  420. PCCPF_TRACE_HEADER Trace;
  421. NTSTATUS Status;
  422. KIRQL OrigIrql;
  423. PSECTION_OBJECT_POINTERS SectionObjectPointer;
  424. LONG FoundIndex;
  425. LONG AvailIndex;
  426. PCCPF_SECTION_INFO SectionInfo;
  427. BOOLEAN IncrementedNumSections;
  428. LONG NewNumSections;
  429. LONG NewNumFaults;
  430. LONG NewNumEntries;
  431. ULONG NumHashLookups;
  432. PCCPF_LOG_ENTRIES TraceBuffer;
  433. PCCPF_LOG_ENTRIES NewTraceBuffer;
  434. PCCPF_LOG_ENTRY LogEntry;
  435. LONG MaxEntries;
  436. PVPB Vpb;
  437. DBGPR((CCPFID,PFTRAC,"CCPF: LogPageFault(%p,%I64x,%x)\n",
  438. FileObject, FileOffset, Flags));
  439. //
  440. // Get the trace associated with this process.
  441. //
  442. Trace = CcPfReferenceProcessTrace(PsGetCurrentProcess());
  443. //
  444. // If there is no trace associated with this process, see if there is
  445. // a system-wide trace.
  446. //
  447. if (Trace == NULL) {
  448. if (CcPfGlobals.SystemWideTrace) {
  449. Trace = CcPfReferenceProcessTrace(PsInitialSystemProcess);
  450. if (Trace) {
  451. CCPF_ASSERT(Trace == CcPfGlobals.SystemWideTrace);
  452. } else {
  453. Status = STATUS_NO_SUCH_MEMBER;
  454. goto cleanup;
  455. }
  456. } else {
  457. Status = STATUS_NO_SUCH_MEMBER;
  458. goto cleanup;
  459. }
  460. }
  461. //
  462. // Make sure the trace is really a trace.
  463. //
  464. CCPF_ASSERT(Trace && Trace->Magic == PF_TRACE_MAGIC_NUMBER);
  465. //
  466. // Don't prefetch ROM-backed pages.
  467. //
  468. if (Flags & CCPF_TYPE_ROM) {
  469. Status = STATUS_NOT_SUPPORTED;
  470. goto cleanup;
  471. }
  472. //
  473. // Check file offset for this page fault. We don't support files >
  474. // 4GB for the prefetcher.
  475. //
  476. if (((PLARGE_INTEGER) &FileOffset)->HighPart != 0) {
  477. Status = STATUS_INVALID_PARAMETER;
  478. goto cleanup;
  479. }
  480. //
  481. // If the volume this file object is on is not mounted, this is probably
  482. // an internal file system file object we don't want to reference.
  483. // Remote file systems may have file objects for which the device object
  484. // does not have a VPB. We don't support prefetching on remote file
  485. // systems.
  486. //
  487. Vpb = FileObject->Vpb;
  488. if (!Vpb) {
  489. Status = STATUS_NOT_SUPPORTED;
  490. goto cleanup;
  491. }
  492. if (!(Vpb->Flags & VPB_MOUNTED)) {
  493. Status = STATUS_DEVICE_NOT_READY;
  494. goto cleanup;
  495. }
  496. //
  497. // Check if the section in which we hit this pagefault is in the
  498. // hash for this trace [so we will have a file name for it]. If
  499. // not we will have to add it.
  500. //
  501. SectionObjectPointer = FileObject->SectionObjectPointer;
  502. NumHashLookups = 0;
  503. IncrementedNumSections = FALSE;
  504. do {
  505. FoundIndex = CcPfLookUpSection(Trace->SectionInfoTable,
  506. Trace->SectionTableSize,
  507. SectionObjectPointer,
  508. &AvailIndex);
  509. if (FoundIndex != CCPF_INVALID_TABLE_INDEX) {
  510. //
  511. // We found the section.
  512. //
  513. break;
  514. }
  515. if (AvailIndex == CCPF_INVALID_TABLE_INDEX) {
  516. //
  517. // We don't have room in the table for anything else. The
  518. // table is allocated so that SectionTableSize >
  519. // MaxSections. This should not be the case.
  520. //
  521. CCPF_ASSERT(FALSE);
  522. Status = STATUS_INSUFFICIENT_RESOURCES;
  523. goto cleanup;
  524. }
  525. //
  526. // We have to add the section. Before we compete for the
  527. // available index, check if we are allowed to have another
  528. // section.
  529. //
  530. if (!IncrementedNumSections) {
  531. NewNumSections = InterlockedIncrement(&Trace->NumSections);
  532. if (NewNumSections > Trace->MaxSections) {
  533. //
  534. // We cannot add any more sections to this trace. So
  535. // we cannot log this page fault.
  536. //
  537. InterlockedDecrement(&Trace->NumSections);
  538. Status = STATUS_INSUFFICIENT_RESOURCES;
  539. goto cleanup;
  540. }
  541. IncrementedNumSections = TRUE;
  542. }
  543. //
  544. // Try to get the available spot for ourselves.
  545. //
  546. SectionInfo = &Trace->SectionInfoTable[AvailIndex];
  547. if (!InterlockedCompareExchange(&SectionInfo->EntryValid, 1, 0)) {
  548. //
  549. // We have to be careful with how we are initializing the
  550. // new entry here. Don't forget, there are no locks.
  551. //
  552. //
  553. // EntryValid was 0 and we set it to 1. It is ours now.
  554. //
  555. //
  556. // First save the other fields of SectionObjectPointers. We check the
  557. // SectionObjectPointer first to find an entry in the hash.
  558. //
  559. SectionInfo->DataSectionObject = SectionObjectPointer->DataSectionObject;
  560. SectionInfo->ImageSectionObject = SectionObjectPointer->ImageSectionObject;
  561. SectionInfo->SectionObjectPointer = SectionObjectPointer;
  562. //
  563. // In case we have to queue a worker to get the name for
  564. // this section, try to get another reference to the trace
  565. // up front. We already hold a reference so we don't have
  566. // to acquire any locks.
  567. //
  568. Status = CcPfAddRef(&Trace->RefCount);
  569. if (NT_SUCCESS(Status)) {
  570. //
  571. // Reference the file object, so it does not go away until
  572. // we get a name for it.
  573. //
  574. ObReferenceObject(FileObject);
  575. SectionInfo->ReferencedFileObject = FileObject;
  576. //
  577. // Push this section into the list for which the worker
  578. // will get file names. Do this before checking to see if
  579. // a worker needs to be queued.
  580. //
  581. InterlockedPushEntrySList(&Trace->SectionsWithoutNamesList,
  582. &SectionInfo->GetNameLink);
  583. //
  584. // If there is not already a worker queued to get
  585. // names, queue one.
  586. //
  587. if (!InterlockedCompareExchange(&Trace->GetFileNameWorkItemQueued,
  588. 1,
  589. 0)) {
  590. //
  591. // Queue the worker.
  592. //
  593. ExQueueWorkItem(&Trace->GetFileNameWorkItem, DelayedWorkQueue);
  594. } else {
  595. //
  596. // Notify the event that an existing worker may be
  597. // waiting on for new sections.
  598. //
  599. KeSetEvent(&Trace->GetFileNameWorkerEvent,
  600. IO_NO_INCREMENT,
  601. FALSE);
  602. //
  603. // We don't need the reference since we did not
  604. // queue a worker.
  605. //
  606. CcPfDecRef(&Trace->RefCount);
  607. }
  608. } else {
  609. //
  610. // We added the section but the trace has already
  611. // ended. We will not be able to get a file name for
  612. // this section. Fall through to log the entry. The
  613. // entry will be ignored though because its section
  614. // won't have a file name.
  615. //
  616. }
  617. //
  618. // Break out of the loop.
  619. //
  620. FoundIndex = AvailIndex;
  621. break;
  622. }
  623. //
  624. // We could not have filled up the table, because the table is
  625. // bigger than the maximum allowed size [MaxSections]
  626. //
  627. CCPF_ASSERT((ULONG) Trace->NumSections < Trace->SectionTableSize);
  628. //
  629. // Updated number of times we've looped. We should not have to
  630. // loop more than SectionTableSize. If there is a free entry,
  631. // we should have found it after that many lookups.
  632. //
  633. NumHashLookups++;
  634. } while (NumHashLookups < Trace->SectionTableSize);
  635. //
  636. // FoundIndex is set to the index of the section in the table.
  637. //
  638. if (FoundIndex == CCPF_INVALID_TABLE_INDEX) {
  639. CCPF_ASSERT(FoundIndex != CCPF_INVALID_TABLE_INDEX);
  640. Status = STATUS_INSUFFICIENT_RESOURCES;
  641. goto cleanup;
  642. }
  643. //
  644. // If the section is a metafile (e.g. directory) section, don't need to
  645. // log more faults for it. We just need to know that we accessed it since
  646. // we can only prefetch all or nothing from metafile.
  647. //
  648. if (Trace->SectionInfoTable[FoundIndex].Metafile) {
  649. Status = STATUS_SUCCESS;
  650. goto cleanup;
  651. }
  652. //
  653. // See if we've already logged too many faults.
  654. //
  655. NewNumFaults = InterlockedIncrement(&Trace->NumFaults);
  656. //
  657. // If we are beyond bounds we cannot log anymore.
  658. //
  659. if (NewNumFaults > Trace->MaxFaults) {
  660. InterlockedDecrement(&Trace->NumFaults);
  661. //
  662. // Try to queue the end of trace workitem.
  663. //
  664. if (!Trace->EndTraceCalled &&
  665. !InterlockedCompareExchange(&Trace->EndTraceCalled, 1, 0)) {
  666. //
  667. // We set EndTraceCalled from 0 to 1. We can queue the
  668. // workitem now.
  669. //
  670. ExQueueWorkItem(&Trace->EndTraceWorkItem, DelayedWorkQueue);
  671. }
  672. Status = STATUS_INSUFFICIENT_RESOURCES;
  673. goto cleanup;
  674. }
  675. //
  676. // Get space for the entry we are going to log.
  677. //
  678. do {
  679. TraceBuffer = Trace->CurrentTraceBuffer;
  680. NewNumEntries = InterlockedIncrement(&TraceBuffer->NumEntries);
  681. //
  682. // If we are beyond bounds, try to allocate a new buffer.
  683. //
  684. if (NewNumEntries > TraceBuffer->MaxEntries) {
  685. InterlockedDecrement(&TraceBuffer->NumEntries);
  686. //
  687. // Allocate a new trace buffer.
  688. //
  689. MaxEntries = CCPF_TRACE_BUFFER_MAX_ENTRIES;
  690. NewTraceBuffer = ExAllocatePoolWithTag(NonPagedPool,
  691. CCPF_TRACE_BUFFER_SIZE,
  692. CCPF_ALLOC_TRCBUF_TAG);
  693. if (NewTraceBuffer == NULL) {
  694. //
  695. // Couldn't allocate a new buffer. Decrement the count
  696. // of logged faults and go away.
  697. //
  698. InterlockedDecrement(&Trace->NumFaults);
  699. Status = STATUS_INSUFFICIENT_RESOURCES;
  700. break;
  701. }
  702. //
  703. // Acquire the right to make trace buffer changes.
  704. //
  705. KeAcquireSpinLock(&Trace->TraceBufferSpinLock, &OrigIrql);
  706. //
  707. // If the trace buffer has already been changed, start over.
  708. //
  709. if (Trace->CurrentTraceBuffer != TraceBuffer) {
  710. KeReleaseSpinLock(&Trace->TraceBufferSpinLock, OrigIrql);
  711. ExFreePool(NewTraceBuffer);
  712. continue;
  713. }
  714. //
  715. // Number of entries field of the full trace buffer should
  716. // be equal to or greater than max entries, because
  717. // somebody may have bumped it just to see it can't log
  718. // its entry here. It should not be less than, however.
  719. //
  720. CCPF_ASSERT(TraceBuffer->NumEntries >= TraceBuffer->MaxEntries);
  721. //
  722. // Initialize the new trace buffer.
  723. //
  724. NewTraceBuffer->NumEntries = 0;
  725. NewTraceBuffer->MaxEntries = MaxEntries;
  726. //
  727. // Insert it at the end of buffers list.
  728. //
  729. InsertTailList(&Trace->TraceBuffersList,
  730. &NewTraceBuffer->TraceBuffersLink);
  731. Trace->NumTraceBuffers++;
  732. //
  733. // Make it the current buffer.
  734. //
  735. Trace->CurrentTraceBuffer = NewTraceBuffer;
  736. //
  737. // Release the spinlock and start over.
  738. //
  739. KeReleaseSpinLock(&Trace->TraceBufferSpinLock, OrigIrql);
  740. continue;
  741. }
  742. LogEntry = &TraceBuffer->Entries[NewNumEntries - 1];
  743. LogEntry->FileOffset = (ULONG) FileOffset;
  744. LogEntry->SectionId = (USHORT) FoundIndex;
  745. LogEntry->IsImage = (Flags & CCPF_TYPE_IMAGE)? TRUE : FALSE;
  746. break;
  747. } while (TRUE);
  748. Status = STATUS_SUCCESS;
  749. cleanup:
  750. if (Trace != NULL) {
  751. CcPfDecRef(&Trace->RefCount);
  752. }
  753. DBGPR((CCPFID,PFTRAC,"CCPF: LogPageFault()=%x\n", Status));
  754. return;
  755. }
  756. NTSTATUS
  757. CcPfQueryPrefetcherInformation (
  758. IN SYSTEM_INFORMATION_CLASS SystemInformationClass,
  759. IN PVOID SystemInformation,
  760. IN ULONG SystemInformationLength,
  761. IN KPROCESSOR_MODE PreviousMode,
  762. OUT PULONG Length
  763. )
  764. /*++
  765. Routine Description:
  766. This routine gets called from NtQuerySystemInformation for
  767. prefetcher related queries.
  768. Arguments:
  769. SystemInformationClass - The system information class about which
  770. to retrieve information.
  771. SystemInformation - A pointer to a buffer which receives the specified
  772. information.
  773. SystemInformationLength - Specifies the length in bytes of the system
  774. information buffer.
  775. PreviousMode - Previous processor mode.
  776. Length - Size of data put into SystemInformation buffer.
  777. Return Value:
  778. Status.
  779. Environment:
  780. Kernel mode. IRQL == PASSIVE_LEVEL.
  781. --*/
  782. {
  783. PPREFETCHER_INFORMATION PrefetcherInformation;
  784. NTSTATUS Status;
  785. PF_SYSTEM_PREFETCH_PARAMETERS Temp;
  786. PKTHREAD CurrentThread;
  787. UNREFERENCED_PARAMETER (SystemInformationClass);
  788. DBGPR((CCPFID,PFTRC,"CCPF: QueryPrefetcherInformation()\n"));
  789. //
  790. // Check permissions.
  791. //
  792. if (!SeSinglePrivilegeCheck(SeProfileSingleProcessPrivilege,PreviousMode)) {
  793. Status = STATUS_ACCESS_DENIED;
  794. goto cleanup;
  795. }
  796. //
  797. // Check parameters.
  798. //
  799. if (SystemInformationLength != sizeof(PREFETCHER_INFORMATION)) {
  800. Status = STATUS_INFO_LENGTH_MISMATCH;
  801. goto cleanup;
  802. }
  803. PrefetcherInformation = SystemInformation;
  804. //
  805. // Verify version and magic.
  806. //
  807. if (PrefetcherInformation->Version != PF_CURRENT_VERSION ||
  808. PrefetcherInformation->Magic != PF_SYSINFO_MAGIC_NUMBER) {
  809. Status = STATUS_INVALID_PARAMETER;
  810. goto cleanup;
  811. }
  812. //
  813. // Process requested information class.
  814. //
  815. switch (PrefetcherInformation->PrefetcherInformationClass) {
  816. case PrefetcherRetrieveTrace:
  817. Status = CcPfGetCompletedTrace(PrefetcherInformation->PrefetcherInformation,
  818. PrefetcherInformation->PrefetcherInformationLength,
  819. Length);
  820. break;
  821. case PrefetcherSystemParameters:
  822. //
  823. // Make sure input buffer is big enough.
  824. //
  825. if (PrefetcherInformation->PrefetcherInformationLength !=
  826. sizeof(PF_SYSTEM_PREFETCH_PARAMETERS)) {
  827. Status = STATUS_BUFFER_TOO_SMALL;
  828. break;
  829. }
  830. //
  831. // Acquire parameters lock and copy current parameters into
  832. // user's buffer.
  833. //
  834. Status = STATUS_SUCCESS;
  835. CurrentThread = KeGetCurrentThread ();
  836. KeEnterCriticalRegionThread(CurrentThread);
  837. ExAcquireResourceSharedLite(&CcPfGlobals.Parameters.ParametersLock, TRUE);
  838. RtlCopyMemory(&Temp,
  839. &CcPfGlobals.Parameters.Parameters,
  840. sizeof(PF_SYSTEM_PREFETCH_PARAMETERS));
  841. ExReleaseResourceLite(&CcPfGlobals.Parameters.ParametersLock);
  842. KeLeaveCriticalRegionThread(CurrentThread);
  843. try {
  844. //
  845. // If called from user-mode, probe whether it is safe to write
  846. // to the pointer passed in.
  847. //
  848. if (PreviousMode != KernelMode) {
  849. ProbeForWriteSmallStructure(PrefetcherInformation->PrefetcherInformation,
  850. sizeof(PF_SYSTEM_PREFETCH_PARAMETERS),
  851. _alignof(PF_SYSTEM_PREFETCH_PARAMETERS));
  852. }
  853. RtlCopyMemory(PrefetcherInformation->PrefetcherInformation,
  854. &Temp,
  855. sizeof(PF_SYSTEM_PREFETCH_PARAMETERS));
  856. } except (EXCEPTION_EXECUTE_HANDLER) {
  857. Status = GetExceptionCode();
  858. }
  859. //
  860. // Set returned number of bytes.
  861. //
  862. if (NT_SUCCESS(Status)) {
  863. if (Length) {
  864. *Length = sizeof(PF_SYSTEM_PREFETCH_PARAMETERS);
  865. }
  866. }
  867. break;
  868. default:
  869. Status = STATUS_INVALID_INFO_CLASS;
  870. }
  871. //
  872. // Fall through with status from switch statement.
  873. //
  874. cleanup:
  875. DBGPR((CCPFID,PFTRC,"CCPF: QueryPrefetcherInformation()=%x\n", Status));
  876. return Status;
  877. }
  878. NTSTATUS
  879. CcPfSetPrefetcherInformation (
  880. IN SYSTEM_INFORMATION_CLASS SystemInformationClass,
  881. IN PVOID SystemInformation,
  882. IN ULONG SystemInformationLength,
  883. IN KPROCESSOR_MODE PreviousMode
  884. )
  885. /*++
  886. Routine Description:
  887. This routine gets called from NtSetSystemInformation for
  888. prefetcher related settings.
  889. Arguments:
  890. SystemInformationClass - The system information which is to be
  891. modified.
  892. SystemInformation - A pointer to a buffer which contains the specified
  893. information.
  894. SystemInformationLength - Specifies the length in bytes of the system
  895. information buffer.
  896. PreviousMode - Previous processor mode.
  897. Return Value:
  898. Status.
  899. Environment:
  900. Kernel mode. IRQL == PASSIVE_LEVEL.
  901. --*/
  902. {
  903. PPREFETCHER_INFORMATION PrefetcherInformation;
  904. PCCPF_PREFETCHER_PARAMETERS PrefetcherParameters;
  905. PF_SYSTEM_PREFETCH_PARAMETERS Parameters;
  906. NTSTATUS Status;
  907. PF_BOOT_PHASE_ID NewPhaseId;
  908. PKTHREAD CurrentThread;
  909. UNREFERENCED_PARAMETER (SystemInformationClass);
  910. DBGPR((CCPFID,PFTRC,"CCPF: SetPrefetcherInformation()\n"));
  911. //
  912. // Check permissions.
  913. //
  914. if (!SeSinglePrivilegeCheck(SeProfileSingleProcessPrivilege,PreviousMode)) {
  915. Status = STATUS_ACCESS_DENIED;
  916. goto cleanup;
  917. }
  918. //
  919. // Check parameters.
  920. //
  921. if (SystemInformationLength != sizeof(PREFETCHER_INFORMATION)) {
  922. Status = STATUS_INFO_LENGTH_MISMATCH;
  923. goto cleanup;
  924. }
  925. PrefetcherInformation = SystemInformation;
  926. //
  927. // Verify version and magic.
  928. //
  929. if (PrefetcherInformation->Version != PF_CURRENT_VERSION ||
  930. PrefetcherInformation->Magic != PF_SYSINFO_MAGIC_NUMBER) {
  931. Status = STATUS_INVALID_PARAMETER;
  932. goto cleanup;
  933. }
  934. //
  935. // Process requested information class.
  936. //
  937. switch (PrefetcherInformation->PrefetcherInformationClass) {
  938. case PrefetcherRetrieveTrace:
  939. Status = STATUS_INVALID_INFO_CLASS;
  940. break;
  941. case PrefetcherSystemParameters:
  942. //
  943. // Make sure input buffer is the right size.
  944. //
  945. if (PrefetcherInformation->PrefetcherInformationLength !=
  946. sizeof(PF_SYSTEM_PREFETCH_PARAMETERS)) {
  947. Status = STATUS_BUFFER_TOO_SMALL;
  948. break;
  949. }
  950. //
  951. // *Copy* the parameters, in case the caller changes them
  952. // beneath our feet to break us.
  953. //
  954. Status = STATUS_SUCCESS;
  955. try {
  956. //
  957. // If called from user-mode, probe whether it is safe to read
  958. // from the pointer passed in.
  959. //
  960. if (PreviousMode != KernelMode) {
  961. ProbeForReadSmallStructure(PrefetcherInformation->PrefetcherInformation,
  962. sizeof(PF_SYSTEM_PREFETCH_PARAMETERS),
  963. _alignof(PF_SYSTEM_PREFETCH_PARAMETERS));
  964. }
  965. RtlCopyMemory(&Parameters,
  966. PrefetcherInformation->PrefetcherInformation,
  967. sizeof(PF_SYSTEM_PREFETCH_PARAMETERS));
  968. } except (EXCEPTION_EXECUTE_HANDLER) {
  969. Status = GetExceptionCode();
  970. }
  971. if (!NT_SUCCESS(Status)) {
  972. break;
  973. }
  974. //
  975. // Verify new parameters.
  976. //
  977. Status = CcPfParametersVerify(&Parameters);
  978. if (!NT_SUCCESS(Status)) {
  979. break;
  980. }
  981. //
  982. // Acquire the parameters lock exclusive.
  983. //
  984. PrefetcherParameters = &CcPfGlobals.Parameters;
  985. CurrentThread = KeGetCurrentThread ();
  986. KeEnterCriticalRegionThread(CurrentThread);
  987. ExAcquireResourceExclusiveLite(&PrefetcherParameters->ParametersLock, TRUE);
  988. //
  989. // Copy them over to our globals.
  990. //
  991. PrefetcherParameters->Parameters = Parameters;
  992. //
  993. // Release the exclusive hold on parameters lock.
  994. //
  995. ExReleaseResourceLite(&PrefetcherParameters->ParametersLock);
  996. KeLeaveCriticalRegionThread(CurrentThread);
  997. //
  998. // Determine if prefetching is still enabled.
  999. //
  1000. CcPfDetermineEnablePrefetcher();
  1001. //
  1002. // Set the event so the service queries for the latest
  1003. // parameters.
  1004. //
  1005. CcPfParametersSetChangedEvent(PrefetcherParameters);
  1006. //
  1007. // If the parameters update was successful, update the registry.
  1008. //
  1009. Status = CcPfParametersSave(PrefetcherParameters);
  1010. break;
  1011. case PrefetcherBootPhase:
  1012. //
  1013. // This is called to notify the prefetcher that a new boot
  1014. // phase has started. The new phase id is at PrefetcherInformation.
  1015. //
  1016. //
  1017. // Check length of PrefetcherInformation.
  1018. //
  1019. if (PrefetcherInformation->PrefetcherInformationLength != sizeof(PF_BOOT_PHASE_ID)) {
  1020. Status = STATUS_BUFFER_TOO_SMALL;
  1021. break;
  1022. }
  1023. //
  1024. // Get new phase id.
  1025. //
  1026. Status = STATUS_SUCCESS;
  1027. try {
  1028. //
  1029. // If called from user-mode, probe whether it is safe to read
  1030. // from the pointer passed in.
  1031. //
  1032. if (PreviousMode != KernelMode) {
  1033. ProbeForReadSmallStructure(PrefetcherInformation->PrefetcherInformation,
  1034. sizeof(PF_BOOT_PHASE_ID),
  1035. _alignof(PF_BOOT_PHASE_ID));
  1036. }
  1037. NewPhaseId = *((PPF_BOOT_PHASE_ID)(PrefetcherInformation->PrefetcherInformation));
  1038. } except (EXCEPTION_EXECUTE_HANDLER) {
  1039. Status = GetExceptionCode();
  1040. }
  1041. if (NT_SUCCESS(Status)) {
  1042. //
  1043. // Call the function to note the new boot phase.
  1044. //
  1045. Status = CcPfBeginBootPhase(NewPhaseId);
  1046. }
  1047. break;
  1048. default:
  1049. Status = STATUS_INVALID_INFO_CLASS;
  1050. }
  1051. //
  1052. // Fall through with status from the switch statement.
  1053. //
  1054. cleanup:
  1055. DBGPR((CCPFID,PFTRC,"CCPF: SetPrefetcherInformation()=%x\n", Status));
  1056. return Status;
  1057. }
  1058. //
  1059. // Internal prefetcher routines:
  1060. //
  1061. //
  1062. // Routines used in prefetch tracing.
  1063. //
  1064. NTSTATUS
  1065. CcPfBeginTrace(
  1066. IN PF_SCENARIO_ID *ScenarioId,
  1067. IN PF_SCENARIO_TYPE ScenarioType,
  1068. IN PEPROCESS Process
  1069. )
  1070. /*++
  1071. Routine Description:
  1072. This function is called to begin tracing for a prefetch scenario.
  1073. Arguments:
  1074. ScenarioId - Identifier for the scenario.
  1075. ScenarioType - Type of scenario.
  1076. Process - The process new scenario is associated with.
  1077. Return Value:
  1078. Status.
  1079. Environment:
  1080. Kernel mode, IRQL == PASSIVE_LEVEL.
  1081. --*/
  1082. {
  1083. PCCPF_TRACE_HEADER Trace;
  1084. PPF_TRACE_LIMITS TraceLimits;
  1085. NTSTATUS Status;
  1086. ULONG AllocationSize;
  1087. ULONG SectionTableSize;
  1088. LONG MaxEntries;
  1089. //
  1090. // Initialize locals.
  1091. //
  1092. Trace = NULL;
  1093. DBGPR((CCPFID,PFTRC,"CCPF: BeginTrace()-%d-%d\n",
  1094. CcPfNumActiveTraces, CcPfGlobals.NumCompletedTraces));
  1095. //
  1096. // Check if prefetching is enabled.
  1097. //
  1098. if (!CCPF_IS_PREFETCHER_ENABLED()) {
  1099. Status = STATUS_NOT_SUPPORTED;
  1100. goto cleanup;
  1101. }
  1102. //
  1103. // Make sure the scenario type is valid.
  1104. //
  1105. if (ScenarioType >= PfMaxScenarioType) {
  1106. Status = STATUS_INVALID_PARAMETER;
  1107. goto cleanup;
  1108. }
  1109. //
  1110. // Check if prefetching is enabled for the specified scenario type.
  1111. //
  1112. if (CcPfGlobals.Parameters.Parameters.EnableStatus[ScenarioType] != PfSvEnabled) {
  1113. Status = STATUS_NOT_SUPPORTED;
  1114. goto cleanup;
  1115. }
  1116. //
  1117. // Check if a system-wide trace is active. If so only it can be active.
  1118. //
  1119. if (CcPfGlobals.SystemWideTrace) {
  1120. Status = STATUS_USER_EXISTS;
  1121. goto cleanup;
  1122. }
  1123. //
  1124. // Make a quick check to see if we already have too many outstanding
  1125. // traces. Since we don't make this check under a lock, the limit is
  1126. // not enforced exactly.
  1127. //
  1128. if ((ULONG)CcPfNumActiveTraces >= CcPfGlobals.Parameters.Parameters.MaxNumActiveTraces) {
  1129. Status = STATUS_TOO_MANY_SESSIONS;
  1130. goto cleanup;
  1131. }
  1132. //
  1133. // Make a quick check to see if we already have too many completed
  1134. // traces that the service has not picked up.
  1135. //
  1136. if ((ULONG)CcPfGlobals.NumCompletedTraces >= CcPfGlobals.Parameters.Parameters.MaxNumSavedTraces) {
  1137. Status = STATUS_TOO_MANY_SESSIONS;
  1138. goto cleanup;
  1139. }
  1140. //
  1141. // If a process was not specified we cannot start a trace.
  1142. //
  1143. if (!Process) {
  1144. Status = STATUS_NOT_SUPPORTED;
  1145. goto cleanup;
  1146. }
  1147. //
  1148. // Allocate and initialize trace structure.
  1149. //
  1150. Trace = ExAllocatePoolWithTag(NonPagedPool,
  1151. sizeof(CCPF_TRACE_HEADER),
  1152. CCPF_ALLOC_TRACE_TAG);
  1153. if (!Trace) {
  1154. Status = STATUS_INSUFFICIENT_RESOURCES;
  1155. goto cleanup;
  1156. }
  1157. //
  1158. // Zero the whole structure so that we don't have to write zeroes
  1159. // one field at a time to initialize it. Note that most fields
  1160. // really have to be initialized to 0's.
  1161. //
  1162. RtlZeroMemory(Trace, sizeof(CCPF_TRACE_HEADER));
  1163. //
  1164. // Initialize other trace fields so we know what to cleanup.
  1165. //
  1166. Trace->Magic = PF_TRACE_MAGIC_NUMBER;
  1167. KeInitializeTimer(&Trace->TraceTimer);
  1168. InitializeListHead(&Trace->TraceBuffersList);
  1169. KeInitializeSpinLock(&Trace->TraceBufferSpinLock);
  1170. InitializeListHead(&Trace->VolumeList);
  1171. Trace->TraceDumpStatus = STATUS_NOT_COMMITTED;
  1172. KeQuerySystemTime(&Trace->LaunchTime);
  1173. //
  1174. // Initialize the spinlock and DPC for the trace timer.
  1175. //
  1176. KeInitializeSpinLock(&Trace->TraceTimerSpinLock);
  1177. KeInitializeDpc(&Trace->TraceTimerDpc,
  1178. CcPfTraceTimerRoutine,
  1179. Trace);
  1180. //
  1181. // Initialize reference count structure. A reference to a trace
  1182. // can only be acquired while holding the active traces spinlock.
  1183. //
  1184. CcPfInitializeRefCount(&Trace->RefCount);
  1185. //
  1186. // Get reference to associated process so it does
  1187. // not go away while our timer routines etc. are running.
  1188. //
  1189. ObReferenceObject(Process);
  1190. Trace->Process = Process;
  1191. //
  1192. // Initialize the workitem that may be queued to call end trace
  1193. // function and the field that has to be InterlockedCompareExchange'd
  1194. // to 1 before anybody queues the workitem or makes the call.
  1195. //
  1196. ExInitializeWorkItem(&Trace->EndTraceWorkItem,
  1197. CcPfEndTraceWorkerThreadRoutine,
  1198. Trace);
  1199. Trace->EndTraceCalled = 0;
  1200. //
  1201. // Initialize the workitem queued to get names for file objects.
  1202. //
  1203. ExInitializeWorkItem(&Trace->GetFileNameWorkItem,
  1204. CcPfGetFileNamesWorkerRoutine,
  1205. Trace);
  1206. Trace->GetFileNameWorkItemQueued = 0;
  1207. KeInitializeEvent(&Trace->GetFileNameWorkerEvent,
  1208. SynchronizationEvent,
  1209. FALSE);
  1210. //
  1211. // Initialize the list where we put sections we have to get names
  1212. // for.
  1213. //
  1214. InitializeSListHead(&Trace->SectionsWithoutNamesList);
  1215. //
  1216. // Initialize scenario id and type fields.
  1217. //
  1218. Trace->ScenarioId = *ScenarioId;
  1219. Trace->ScenarioType = ScenarioType;
  1220. //
  1221. // Determine trace limits and timer period from scenario type.
  1222. // We have already checked that ScenarioType is within limits.
  1223. //
  1224. TraceLimits = &CcPfGlobals.Parameters.Parameters.TraceLimits[Trace->ScenarioType];
  1225. Trace->MaxFaults = TraceLimits->MaxNumPages;
  1226. Trace->MaxSections = TraceLimits->MaxNumSections;
  1227. Trace->TraceTimerPeriod.QuadPart = TraceLimits->TimerPeriod;
  1228. //
  1229. // Make sure the sizes are within sanity limits.
  1230. //
  1231. if ((Trace->MaxFaults == 0) || (Trace->MaxSections == 0)) {
  1232. Status = STATUS_INVALID_PARAMETER;
  1233. goto cleanup;
  1234. }
  1235. if (Trace->MaxFaults > PF_MAXIMUM_LOG_ENTRIES) {
  1236. Trace->MaxFaults = PF_MAXIMUM_LOG_ENTRIES;
  1237. }
  1238. if (Trace->MaxSections > PF_MAXIMUM_SECTIONS) {
  1239. Trace->MaxSections = PF_MAXIMUM_SECTIONS;
  1240. }
  1241. //
  1242. // Allocate a trace buffer and section info table.
  1243. //
  1244. MaxEntries = CCPF_TRACE_BUFFER_MAX_ENTRIES;
  1245. Trace->CurrentTraceBuffer = ExAllocatePoolWithTag(NonPagedPool,
  1246. CCPF_TRACE_BUFFER_SIZE,
  1247. CCPF_ALLOC_TRCBUF_TAG);
  1248. if (Trace->CurrentTraceBuffer == NULL) {
  1249. Status = STATUS_INSUFFICIENT_RESOURCES;
  1250. goto cleanup;
  1251. }
  1252. Trace->CurrentTraceBuffer->NumEntries = 0;
  1253. Trace->CurrentTraceBuffer->MaxEntries = MaxEntries;
  1254. //
  1255. // Insert the current trace buffer to the trace buffers list.
  1256. //
  1257. InsertTailList(&Trace->TraceBuffersList,
  1258. &Trace->CurrentTraceBuffer->TraceBuffersLink);
  1259. Trace->NumTraceBuffers = 1;
  1260. //
  1261. // SectionInfoTable is a hash. To give it enough room and avoid
  1262. // too many hash conflicts, allocate it to be bigger.
  1263. //
  1264. SectionTableSize = Trace->MaxSections + (Trace->MaxSections / 2);
  1265. AllocationSize = SectionTableSize * sizeof(CCPF_SECTION_INFO);
  1266. Trace->SectionInfoTable = ExAllocatePoolWithTag(NonPagedPool,
  1267. AllocationSize,
  1268. CCPF_ALLOC_SECTTBL_TAG);
  1269. if (!Trace->SectionInfoTable) {
  1270. Status = STATUS_INSUFFICIENT_RESOURCES;
  1271. goto cleanup;
  1272. }
  1273. Trace->SectionTableSize = SectionTableSize;
  1274. //
  1275. // Initialize entries in the section table. We want the whole table
  1276. // to contain zeroes, so just use RtlZeroMemory.
  1277. //
  1278. // EntryValid is the crucial field in section info entries allowing
  1279. // us not to have any locks. The first to (interlocked) set it
  1280. // to 1 gets the entry in the table. In case someone tries to
  1281. // access the entry right afterwards we initialize the other
  1282. // fields to sensible values upfront.
  1283. //
  1284. // It is important to set SectionObjectPointer to NULL. When EntryValid is
  1285. // InterlockedCompareExchange'd into 1, we don't want anybody
  1286. // to match before we set it up.
  1287. //
  1288. RtlZeroMemory(Trace->SectionInfoTable, AllocationSize);
  1289. //
  1290. // Add this trace to active traces list.
  1291. // Set the trace on process header.
  1292. // Start the trace timer.
  1293. // We'll start logging page faults, processing process delete notificatios etc.
  1294. //
  1295. CcPfActivateTrace(Trace);
  1296. //
  1297. // NOTE: FROM THIS POINT ON WE SHOULD NOT FAIL.
  1298. // CcPfEndTrace has to be called to stop & cleanup the trace.
  1299. //
  1300. Status = STATUS_SUCCESS;
  1301. cleanup:
  1302. if (!NT_SUCCESS(Status)) {
  1303. if (Trace) {
  1304. CcPfCleanupTrace(Trace);
  1305. ExFreePool(Trace);
  1306. }
  1307. }
  1308. DBGPR((CCPFID,PFTRC,"CCPF: BeginTrace(%p)=%x\n", Trace, Status));
  1309. return Status;
  1310. }
  1311. NTSTATUS
  1312. CcPfActivateTrace(
  1313. IN PCCPF_TRACE_HEADER Trace
  1314. )
  1315. /*++
  1316. Routine Description:
  1317. This routine adds the specified trace to the list of active
  1318. traces.
  1319. Arguments:
  1320. Trace - Pointer to trace header.
  1321. Return Value:
  1322. STATUS_SUCCESS.
  1323. Environment:
  1324. Kernel mode, IRQL == PASSIVE_LEVEL. Acquires spinlock.
  1325. --*/
  1326. {
  1327. KIRQL OrigIrql;
  1328. NTSTATUS Status;
  1329. BOOLEAN TimerAlreadyQueued;
  1330. DBGPR((CCPFID,PFTRC,"CCPF: ActivateTrace(%p)\n", Trace));
  1331. //
  1332. // Get a reference to the trace for the timer.
  1333. //
  1334. Status = CcPfAddRef(&Trace->RefCount);
  1335. CCPF_ASSERT(NT_SUCCESS(Status));
  1336. //
  1337. // Insert to active traces list.
  1338. //
  1339. KeAcquireSpinLock(&CcPfGlobals.ActiveTracesLock, &OrigIrql);
  1340. //
  1341. // Insert the entry before the found position.
  1342. //
  1343. InsertTailList(&CcPfGlobals.ActiveTraces, &Trace->ActiveTracesLink);
  1344. CcPfNumActiveTraces++;
  1345. //
  1346. // Start the timer.
  1347. //
  1348. TimerAlreadyQueued = KeSetTimer(&Trace->TraceTimer,
  1349. Trace->TraceTimerPeriod,
  1350. &Trace->TraceTimerDpc);
  1351. //
  1352. // We just initialized the timer. It could not have been already queued.
  1353. //
  1354. CCPF_ASSERT(!TimerAlreadyQueued);
  1355. //
  1356. // Set up the trace pointer on the process with fast ref. Since we are
  1357. // already holding a reference, this operation should not fail.
  1358. //
  1359. Status = CcPfAddProcessTrace(Trace->Process, Trace);
  1360. CCPF_ASSERT(NT_SUCCESS(Status));
  1361. //
  1362. // Do we trace system-wide for this scenario type?
  1363. //
  1364. if (CCPF_IS_SYSTEM_WIDE_SCENARIO_TYPE(Trace->ScenarioType)) {
  1365. CcPfGlobals.SystemWideTrace = Trace;
  1366. } else {
  1367. //
  1368. // If we are the only active trace, place ourselves on the system
  1369. // process as well so we can trace ReadFile & metafile access.
  1370. //
  1371. if (CcPfNumActiveTraces == 1) {
  1372. Status = CcPfAddProcessTrace(PsInitialSystemProcess, Trace);
  1373. CCPF_ASSERT(NT_SUCCESS(Status));
  1374. }
  1375. }
  1376. //
  1377. // NOTE: AddProcessTrace and KeSetTimer(TraceTimer) has to be done
  1378. // inside the spinlock so DeactivateTrace can know activation has been
  1379. // fully completed by acquiring and releasing the spinlock.
  1380. //
  1381. KeReleaseSpinLock(&CcPfGlobals.ActiveTracesLock, OrigIrql);
  1382. return STATUS_SUCCESS;
  1383. }
  1384. NTSTATUS
  1385. CcPfDeactivateTrace(
  1386. IN PCCPF_TRACE_HEADER Trace
  1387. )
  1388. /*++
  1389. Routine Description:
  1390. This routine waits for all references to the trace to go away, and
  1391. removes it from the active traces list. This function should only
  1392. be called after CcPfActivateTrace has been called on the trace.
  1393. Arguments:
  1394. Trace - Pointer to trace header.
  1395. Return Value:
  1396. STATUS_SUCCESS.
  1397. Environment:
  1398. Kernel mode, IRQL == PASSIVE_LEVEL. Acquires spinlock.
  1399. --*/
  1400. {
  1401. PCCPF_TRACE_HEADER RemovedTrace;
  1402. PCCPF_TRACE_HEADER ReferencedTrace;
  1403. KIRQL OrigIrql;
  1404. NTSTATUS Status;
  1405. DBGPR((CCPFID,PFTRC,"CCPF: DeactivateTrace(%p)\n", Trace));
  1406. //
  1407. // Acquire and release the active traces spinlock. This makes sure we
  1408. // don't try to deactivate before activation (which also holds this lock)
  1409. // has fully completed.
  1410. //
  1411. #if !defined (NT_UP)
  1412. KeAcquireSpinLock(&CcPfGlobals.ActiveTracesLock, &OrigIrql);
  1413. KeReleaseSpinLock(&CcPfGlobals.ActiveTracesLock, OrigIrql);
  1414. #endif // NT_UP
  1415. //
  1416. // Remove the trace from process header and release the fast refs.
  1417. //
  1418. RemovedTrace = CcPfRemoveProcessTrace(Trace->Process);
  1419. CCPF_ASSERT(RemovedTrace == Trace);
  1420. //
  1421. // Release the reference associated with the fast ref itself.
  1422. //
  1423. CcPfDecRef(&Trace->RefCount);
  1424. //
  1425. // If we were placed on the system process as well, remove that.
  1426. //
  1427. ReferencedTrace = CcPfReferenceProcessTrace(PsInitialSystemProcess);
  1428. if (ReferencedTrace) {
  1429. if (Trace == ReferencedTrace) {
  1430. //
  1431. // Remove ourselves from the system process header.
  1432. //
  1433. RemovedTrace = CcPfRemoveProcessTrace(PsInitialSystemProcess);
  1434. CCPF_ASSERT(RemovedTrace == Trace);
  1435. //
  1436. // Release the reference associated with the fast ref itself.
  1437. //
  1438. CcPfDecRef(&Trace->RefCount);
  1439. }
  1440. //
  1441. // Release the reference we just got.
  1442. //
  1443. CcPfDecRef(&ReferencedTrace->RefCount);
  1444. }
  1445. //
  1446. // Cancel the timer.
  1447. //
  1448. CcPfCancelTraceTimer(Trace);
  1449. //
  1450. // Signal the trace's get-file-name worker to return [in case it
  1451. // is active] and release its reference. Give it a priority bump
  1452. // so it releases its reference before we begin waiting for it.
  1453. //
  1454. KeSetEvent(&Trace->GetFileNameWorkerEvent,
  1455. EVENT_INCREMENT,
  1456. FALSE);
  1457. //
  1458. // Wait for all references to go away.
  1459. //
  1460. Status = CcPfAcquireExclusiveRef(&Trace->RefCount);
  1461. DBGPR((CCPFID,PFTRAC,"CCPF: DeactivateTrace-Exclusive=%x\n", Status));
  1462. //
  1463. // We should have been able to acquire the trace exclusively.
  1464. // Otherwise this trace may have already been deactivated.
  1465. //
  1466. CCPF_ASSERT(NT_SUCCESS(Status));
  1467. //
  1468. // Get the active traces lock.
  1469. //
  1470. KeAcquireSpinLock(&CcPfGlobals.ActiveTracesLock, &OrigIrql);
  1471. //
  1472. // Remove us from the active trace list.
  1473. //
  1474. RemoveEntryList(&Trace->ActiveTracesLink);
  1475. CcPfNumActiveTraces--;
  1476. //
  1477. // If this was a system-wide trace, it is over now.
  1478. //
  1479. if (CCPF_IS_SYSTEM_WIDE_SCENARIO_TYPE(Trace->ScenarioType)) {
  1480. CCPF_ASSERT(CcPfGlobals.SystemWideTrace == Trace);
  1481. CcPfGlobals.SystemWideTrace = NULL;
  1482. }
  1483. //
  1484. // Release active traces lock.
  1485. //
  1486. KeReleaseSpinLock(&CcPfGlobals.ActiveTracesLock, OrigIrql);
  1487. return STATUS_SUCCESS;
  1488. }
  1489. NTSTATUS
  1490. CcPfEndTrace(
  1491. IN PCCPF_TRACE_HEADER Trace
  1492. )
  1493. /*++
  1494. Routine Description:
  1495. This function is called to end a prefetch trace. In order to
  1496. ensure this function gets called only once, EndTraceCalled field
  1497. of the trace has to be InterlockedCompareExchange'd from 0 to
  1498. 1. All intermediate references and allocations are freed. The
  1499. trace is saved until the service queries for it and the service
  1500. event is signaled.
  1501. Arguments:
  1502. Trace - Pointer to trace header.
  1503. Return Value:
  1504. Status.
  1505. Environment:
  1506. Kernel mode. IRQL == PASSIVE_LEVEL.
  1507. --*/
  1508. {
  1509. PCCPF_TRACE_DUMP TraceDump;
  1510. PCCPF_TRACE_DUMP RemovedTraceDump;
  1511. PLIST_ENTRY ListHead;
  1512. OBJECT_ATTRIBUTES EventObjAttr;
  1513. UNICODE_STRING EventName;
  1514. HANDLE EventHandle;
  1515. NTSTATUS Status;
  1516. LONG FaultsLoggedAfterTimeout;
  1517. DBGPR((CCPFID,PFTRC,"CCPF: EndTrace(%p)\n", Trace));
  1518. //
  1519. // Make sure the trace we are called on is valid.
  1520. //
  1521. CCPF_ASSERT(Trace && Trace->Magic == PF_TRACE_MAGIC_NUMBER);
  1522. //
  1523. // Before anyone called us, they should have
  1524. // InterlockedCompareExchange'd this to 1 to ensure this function
  1525. // gets called only once for this trace.
  1526. //
  1527. CCPF_ASSERT(Trace->EndTraceCalled == 1);
  1528. //
  1529. // Deactivate the trace, if necessary waiting for all the references to
  1530. // it to go away.
  1531. // This function makes sure activation fully finished before deactivating.
  1532. // This needs to be done before we do anything else with the trace.
  1533. //
  1534. CcPfDeactivateTrace(Trace);
  1535. //
  1536. // If we did not timeout, save the number of pagefaults logged
  1537. // since the last period into the next period.
  1538. //
  1539. if (Trace->CurPeriod < PF_MAX_NUM_TRACE_PERIODS) {
  1540. //
  1541. // Number of log entries could only have increased since the
  1542. // last time we saved them.
  1543. //
  1544. CCPF_ASSERT(Trace->NumFaults >= Trace->LastNumFaults);
  1545. Trace->FaultsPerPeriod[Trace->CurPeriod] =
  1546. Trace->NumFaults - Trace->LastNumFaults;
  1547. Trace->LastNumFaults = Trace->NumFaults;
  1548. Trace->CurPeriod++;
  1549. } else {
  1550. //
  1551. // If we did time out, we may have logged more faults since we
  1552. // saved the number of faults, until the end trace function
  1553. // got run. Update the number faults in the last period.
  1554. //
  1555. if (Trace->LastNumFaults != Trace->NumFaults) {
  1556. //
  1557. // What we saved as LastNumFaults in the timer routine
  1558. // cannot be greater than what we really logged.
  1559. //
  1560. CCPF_ASSERT(Trace->LastNumFaults < Trace->NumFaults);
  1561. FaultsLoggedAfterTimeout = Trace->NumFaults - Trace->LastNumFaults;
  1562. Trace->FaultsPerPeriod[Trace->CurPeriod - 1] += FaultsLoggedAfterTimeout;
  1563. }
  1564. }
  1565. //
  1566. // Convert the trace into a paged, single buffer dump that we can
  1567. // give to the user mode service.
  1568. //
  1569. Status = CcPfBuildDumpFromTrace(&TraceDump, Trace);
  1570. Trace->TraceDumpStatus = Status;
  1571. Trace->TraceDump = TraceDump;
  1572. //
  1573. // Cleanup and deallocate the trace structure.
  1574. //
  1575. CcPfCleanupTrace(Trace);
  1576. ExFreePool(Trace);
  1577. //
  1578. // If we could not create a dump from the trace we acquired, we
  1579. // are done.
  1580. //
  1581. if (!NT_SUCCESS(Status)) {
  1582. goto cleanup;
  1583. }
  1584. //
  1585. // Put the dump on the saved traces list. If we have too many,
  1586. // trim in a round robin fashion. First get the lock.
  1587. //
  1588. ExAcquireFastMutex(&CcPfGlobals.CompletedTracesLock);
  1589. InsertTailList(&CcPfGlobals.CompletedTraces, &TraceDump->CompletedTracesLink);
  1590. CcPfGlobals.NumCompletedTraces++;
  1591. while ((ULONG) CcPfGlobals.NumCompletedTraces >
  1592. CcPfGlobals.Parameters.Parameters.MaxNumSavedTraces) {
  1593. //
  1594. // While NumCompletedTraces > MaxNumSavedTraces we should have at
  1595. // least a completed trace in the list.
  1596. //
  1597. if (IsListEmpty(&CcPfGlobals.CompletedTraces)) {
  1598. CCPF_ASSERT(FALSE);
  1599. break;
  1600. }
  1601. ListHead = RemoveHeadList(&CcPfGlobals.CompletedTraces);
  1602. RemovedTraceDump = CONTAINING_RECORD(ListHead,
  1603. CCPF_TRACE_DUMP,
  1604. CompletedTracesLink);
  1605. //
  1606. // Free the tracedump structure.
  1607. //
  1608. CCPF_ASSERT(RemovedTraceDump->Trace.MagicNumber == PF_TRACE_MAGIC_NUMBER);
  1609. ExFreePool(RemovedTraceDump);
  1610. CcPfGlobals.NumCompletedTraces--;
  1611. }
  1612. ExReleaseFastMutex(&CcPfGlobals.CompletedTracesLock);
  1613. //
  1614. // Signal the event service is waiting on for new traces. If we
  1615. // have not opened it yet, first we have to open it.
  1616. //
  1617. if (CcPfGlobals.CompletedTracesEvent) {
  1618. ZwSetEvent(CcPfGlobals.CompletedTracesEvent, NULL);
  1619. } else {
  1620. //
  1621. // Try to open the event. We don't open this at initialization
  1622. // because our service may not have started to create this
  1623. // event yet. If csrss.exe has not initialized, we may not
  1624. // even have the BaseNamedObjects object directory created, in
  1625. // which Win32 events reside.
  1626. //
  1627. RtlInitUnicodeString(&EventName, PF_COMPLETED_TRACES_EVENT_NAME);
  1628. InitializeObjectAttributes(&EventObjAttr,
  1629. &EventName,
  1630. OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
  1631. NULL,
  1632. NULL);
  1633. Status = ZwOpenEvent(&EventHandle,
  1634. EVENT_ALL_ACCESS,
  1635. &EventObjAttr);
  1636. if (NT_SUCCESS(Status)) {
  1637. //
  1638. // Acquire the lock and set the global handle.
  1639. //
  1640. ExAcquireFastMutex(&CcPfGlobals.CompletedTracesLock);
  1641. if (!CcPfGlobals.CompletedTracesEvent) {
  1642. //
  1643. // Set the global handle.
  1644. //
  1645. CcPfGlobals.CompletedTracesEvent = EventHandle;
  1646. CCPF_ASSERT(EventHandle);
  1647. } else {
  1648. //
  1649. // Somebody already initialized the global handle
  1650. // before us. Close our handle and use the one they
  1651. // initialized.
  1652. //
  1653. ZwClose(EventHandle);
  1654. }
  1655. ExReleaseFastMutex(&CcPfGlobals.CompletedTracesLock);
  1656. //
  1657. // We have an event now. Signal it.
  1658. //
  1659. ZwSetEvent(CcPfGlobals.CompletedTracesEvent, NULL);
  1660. }
  1661. }
  1662. Status = STATUS_SUCCESS;
  1663. cleanup:
  1664. DBGPR((CCPFID,PFTRC,"CCPF: EndTrace(%p)=%x\n", Trace, Status));
  1665. return Status;
  1666. }
  1667. NTSTATUS
  1668. CcPfBuildDumpFromTrace(
  1669. OUT PCCPF_TRACE_DUMP *TraceDump,
  1670. IN PCCPF_TRACE_HEADER RuntimeTrace
  1671. )
  1672. /*++
  1673. Routine Description:
  1674. This routine allocates (from paged pool) and prepares a TraceDump
  1675. structure from a run-time trace that can be saved on a list. It
  1676. tries to get file names for all sections in the passed in run-time
  1677. trace structure. The file names that are obtained are allocated
  1678. from paged pool and put on the run-time trace's section info table
  1679. and are cleaned up when that is cleaned up. The trace dump
  1680. structure contains a pointer to an allocated (from paged pool)
  1681. trace buffer that was built from the run-time trace, that can be
  1682. passed to the user mode service. The caller is responsible for
  1683. freeing both the TraceDump structure and the prepared trace.
  1684. Arguments:
  1685. TraceDump - Where pointer to the allocated trace buffer is put if
  1686. success is returned. If failure is returned, this is undefined.
  1687. RuntimeTrace - Run-time trace structure to put into dump format.
  1688. Return Value:
  1689. Status.
  1690. Environment:
  1691. Kernel mode, IRQL == PASSIVE_LEVEL.
  1692. --*/
  1693. {
  1694. NTSTATUS Status;
  1695. ULONG SectionIdx;
  1696. PCCPF_SECTION_INFO SectionInfo;
  1697. ULONG FileNameLength;
  1698. ULONG TraceSize;
  1699. PPF_TRACE_HEADER Trace;
  1700. ULONG FileNameDataNumChars;
  1701. PSHORT SectIdTranslationTable;
  1702. ULONG TranslationTableSize;
  1703. PPF_SECTION_INFO TargetSectionInfo;
  1704. LONG EntryIdx;
  1705. LONG NumEntries;
  1706. PCCPF_LOG_ENTRY LogEntry;
  1707. PPF_LOG_ENTRY TargetLogEntry;
  1708. ULONG NumEntriesCopied;
  1709. ULONG NumSectionsCopied;
  1710. PCHAR DestPtr;
  1711. SHORT NewSectionId;
  1712. PPF_LOG_ENTRY NewTraceEntries;
  1713. ULONG SectionInfoSize;
  1714. PCCPF_LOG_ENTRIES TraceBuffer;
  1715. PLIST_ENTRY HeadEntry;
  1716. PLIST_ENTRY NextEntry;
  1717. LONG CurrentFaultIdx;
  1718. LONG CurrentPeriodIdx;
  1719. LONG CurrentPeriodEndFaultIdx;
  1720. ULONG_PTR AlignmentOffset;
  1721. ULONG AllocationSize;
  1722. ULONG NumVolumes;
  1723. ULONG TotalVolumeInfoSize;
  1724. ULONG VolumeInfoSize;
  1725. PCCPF_VOLUME_INFO VolumeInfo;
  1726. PPF_VOLUME_INFO TargetVolumeInfo;
  1727. ULONG FailedCheck;
  1728. //
  1729. // Initialize locals.
  1730. //
  1731. SectIdTranslationTable = NULL;
  1732. Trace = NULL;
  1733. *TraceDump = NULL;
  1734. NumEntriesCopied = 0;
  1735. DBGPR((CCPFID,PFTRC,"CCPF: DumpTrace(%p)\n", RuntimeTrace));
  1736. //
  1737. // If the acquired trace is too small, don't bother.
  1738. //
  1739. if (RuntimeTrace->NumFaults < PF_MIN_SCENARIO_PAGES) {
  1740. Status = STATUS_BUFFER_TOO_SMALL;
  1741. goto cleanup;
  1742. }
  1743. //
  1744. // If the acquired trace does not contain any sections or volumes
  1745. // it is useless.
  1746. //
  1747. if (!RuntimeTrace->NumSections || !RuntimeTrace->NumVolumes) {
  1748. Status = STATUS_BUFFER_TOO_SMALL;
  1749. goto cleanup;
  1750. }
  1751. //
  1752. // Calculate the maximum size of trace we will build.
  1753. //
  1754. TraceSize = sizeof(PF_TRACE_HEADER);
  1755. TraceSize += RuntimeTrace->NumFaults * sizeof(PF_LOG_ENTRY);
  1756. TraceSize += RuntimeTrace->NumSections * sizeof(PF_SECTION_INFO);
  1757. //
  1758. // Add up file name data size.
  1759. //
  1760. FileNameDataNumChars = 0;
  1761. for (SectionIdx = 0;
  1762. SectionIdx < RuntimeTrace->SectionTableSize;
  1763. SectionIdx++) {
  1764. SectionInfo = &RuntimeTrace->SectionInfoTable[SectionIdx];
  1765. if (SectionInfo->EntryValid && SectionInfo->FileName) {
  1766. //
  1767. // We would add space for terminating NUL but the space
  1768. // for one character in section info accounts for that.
  1769. //
  1770. FileNameDataNumChars += wcslen(SectionInfo->FileName);
  1771. }
  1772. }
  1773. TraceSize += FileNameDataNumChars * sizeof(WCHAR);
  1774. //
  1775. // We may have to align LogEntries coming after section infos that
  1776. // contain WCHAR strings.
  1777. //
  1778. TraceSize += _alignof(PF_LOG_ENTRY);
  1779. //
  1780. // Add space for the volume info nodes.
  1781. //
  1782. HeadEntry = &RuntimeTrace->VolumeList;
  1783. NextEntry = HeadEntry->Flink;
  1784. NumVolumes = 0;
  1785. TotalVolumeInfoSize = 0;
  1786. while (NextEntry != HeadEntry) {
  1787. VolumeInfo = CONTAINING_RECORD(NextEntry,
  1788. CCPF_VOLUME_INFO,
  1789. VolumeLink);
  1790. NextEntry = NextEntry->Flink;
  1791. //
  1792. // Keep track of number of volumes on the list so we can
  1793. // verify it.
  1794. //
  1795. NumVolumes++;
  1796. //
  1797. // Calculate size of this volume info in the dumped
  1798. // trace. Note that PF_VOLUME_INFO contains space for the
  1799. // terminating NUL.
  1800. //
  1801. VolumeInfoSize = sizeof(PF_VOLUME_INFO);
  1802. VolumeInfoSize += VolumeInfo->VolumePathLength * sizeof(WCHAR);
  1803. //
  1804. // Update size for the volume info block. Add space for
  1805. // aligning a volume info node if necessary.
  1806. //
  1807. TotalVolumeInfoSize += VolumeInfoSize;
  1808. TotalVolumeInfoSize += _alignof(PF_VOLUME_INFO);
  1809. }
  1810. CCPF_ASSERT(NumVolumes == RuntimeTrace->NumVolumes);
  1811. TraceSize += TotalVolumeInfoSize;
  1812. //
  1813. // Allocate the trace dump structure we are going to
  1814. // return. Subtract sizeof(PF_TRACE_HEADER) since both
  1815. // CCPF_TRACE_DUMP and TraceSize include this.
  1816. //
  1817. AllocationSize = sizeof(CCPF_TRACE_DUMP);
  1818. AllocationSize += TraceSize - sizeof(PF_TRACE_HEADER);
  1819. *TraceDump = ExAllocatePoolWithTag(PagedPool,
  1820. AllocationSize,
  1821. CCPF_ALLOC_TRCDMP_TAG);
  1822. if ((*TraceDump) == NULL) {
  1823. Status = STATUS_INSUFFICIENT_RESOURCES;
  1824. goto cleanup;
  1825. }
  1826. //
  1827. // Get pointer to the trace structure.
  1828. //
  1829. Trace = &(*TraceDump)->Trace;
  1830. //
  1831. // Setup trace header.
  1832. //
  1833. Trace->Version = PF_CURRENT_VERSION;
  1834. Trace->MagicNumber = PF_TRACE_MAGIC_NUMBER;
  1835. Trace->ScenarioId = RuntimeTrace->ScenarioId;
  1836. Trace->ScenarioType = RuntimeTrace->ScenarioType;
  1837. Trace->LaunchTime = RuntimeTrace->LaunchTime;
  1838. Trace->PeriodLength = RuntimeTrace->TraceTimerPeriod.QuadPart;
  1839. //
  1840. // Initialize faults per period to 0's. We will update these as we
  1841. // copy valid entries from the runtime trace.
  1842. //
  1843. RtlZeroMemory(Trace->FaultsPerPeriod, sizeof(Trace->FaultsPerPeriod));
  1844. DestPtr = (PCHAR) Trace + sizeof(PF_TRACE_HEADER);
  1845. //
  1846. // Copy over sections for which we have names. Since their indices
  1847. // in the new table will be different, build a translation table
  1848. // that we will use to translate the section id's of log
  1849. // entries. First allocate this table.
  1850. //
  1851. TranslationTableSize = RuntimeTrace->SectionTableSize * sizeof(USHORT);
  1852. SectIdTranslationTable = ExAllocatePoolWithTag(PagedPool,
  1853. TranslationTableSize,
  1854. CCPF_ALLOC_TRCDMP_TAG);
  1855. if (!SectIdTranslationTable) {
  1856. Status = STATUS_INSUFFICIENT_RESOURCES;
  1857. goto cleanup;
  1858. }
  1859. //
  1860. // Copy section information to the trace buffer while setting up
  1861. // the translation table.
  1862. //
  1863. Trace->SectionInfoOffset = (ULONG) (DestPtr - (PCHAR) Trace);
  1864. NumSectionsCopied = 0;
  1865. for (SectionIdx = 0;
  1866. SectionIdx < RuntimeTrace->SectionTableSize;
  1867. SectionIdx++) {
  1868. SectionInfo = &RuntimeTrace->SectionInfoTable[SectionIdx];
  1869. if (SectionInfo->EntryValid &&
  1870. SectionInfo->FileName &&
  1871. (FileNameLength = wcslen(SectionInfo->FileName)) > 0) {
  1872. TargetSectionInfo = (PPF_SECTION_INFO) DestPtr;
  1873. SectionInfoSize = sizeof(PF_SECTION_INFO);
  1874. SectionInfoSize += FileNameLength * sizeof(WCHAR);
  1875. //
  1876. // Make sure we are not going off bounds.
  1877. //
  1878. if (DestPtr + SectionInfoSize > (PCHAR) Trace + TraceSize) {
  1879. SectIdTranslationTable[SectionIdx] = CCPF_INVALID_TABLE_INDEX;
  1880. CCPF_ASSERT(FALSE);
  1881. continue;
  1882. }
  1883. TargetSectionInfo->FileNameLength = (USHORT) FileNameLength;
  1884. TargetSectionInfo->Metafile = (USHORT) SectionInfo->Metafile;
  1885. //
  1886. // Copy the file name including the terminating NUL.
  1887. //
  1888. RtlCopyMemory(TargetSectionInfo->FileName,
  1889. SectionInfo->FileName,
  1890. (FileNameLength + 1) * sizeof(WCHAR));
  1891. //
  1892. // Update our position in the destination buffer.
  1893. //
  1894. DestPtr += SectionInfoSize;
  1895. //
  1896. // Update the translation table:
  1897. //
  1898. SectIdTranslationTable[SectionIdx] = (USHORT) NumSectionsCopied;
  1899. NumSectionsCopied++;
  1900. } else {
  1901. SectIdTranslationTable[SectionIdx] = CCPF_INVALID_TABLE_INDEX;
  1902. }
  1903. }
  1904. Trace->NumSections = NumSectionsCopied;
  1905. CCPF_ASSERT(Trace->NumSections <= (ULONG) RuntimeTrace->NumSections);
  1906. //
  1907. // Make sure DestPtr is aligned for Log Entries coming next. We
  1908. // had reserved max space we'd need for this adjustment upfront.
  1909. //
  1910. AlignmentOffset = ((ULONG_PTR) DestPtr) % _alignof(PF_LOG_ENTRY);
  1911. if (AlignmentOffset) {
  1912. DestPtr += (_alignof(PF_LOG_ENTRY) - AlignmentOffset);
  1913. }
  1914. //
  1915. // Copy the log entries.
  1916. //
  1917. Trace->TraceBufferOffset = (ULONG) (DestPtr - (PCHAR) Trace);
  1918. NewTraceEntries = (PPF_LOG_ENTRY) DestPtr;
  1919. //
  1920. // Initialize index of the current log entry in the whole runtime
  1921. // trace, which period it was logged in, and what the index of the
  1922. // first fault logged after this period was.
  1923. //
  1924. CurrentFaultIdx = 0;
  1925. CurrentPeriodIdx = 0;
  1926. CurrentPeriodEndFaultIdx = RuntimeTrace->FaultsPerPeriod[0];
  1927. //
  1928. // Walk through the trace buffers list and copy over
  1929. // entries. NumEntriesCopied is initialized to 0 at the top.
  1930. //
  1931. HeadEntry = &RuntimeTrace->TraceBuffersList;
  1932. NextEntry = HeadEntry->Flink;
  1933. while (NextEntry != HeadEntry) {
  1934. TraceBuffer = CONTAINING_RECORD(NextEntry,
  1935. CCPF_LOG_ENTRIES,
  1936. TraceBuffersLink);
  1937. NumEntries = TraceBuffer->NumEntries;
  1938. NextEntry = NextEntry->Flink;
  1939. for (EntryIdx = 0, LogEntry = TraceBuffer->Entries;
  1940. EntryIdx < NumEntries;
  1941. EntryIdx++, LogEntry++, CurrentFaultIdx++) {
  1942. //
  1943. // Current fault index should not be greater than the
  1944. // total number of faults we logged in the trace.
  1945. //
  1946. if (CurrentFaultIdx >= RuntimeTrace->NumFaults) {
  1947. CCPF_ASSERT(FALSE);
  1948. Status = STATUS_INVALID_PARAMETER;
  1949. goto cleanup;
  1950. }
  1951. //
  1952. // Update the period this fault was logged in
  1953. //
  1954. while (CurrentFaultIdx >= CurrentPeriodEndFaultIdx) {
  1955. CurrentPeriodIdx++;
  1956. //
  1957. // Check bounds on period.
  1958. //
  1959. if (CurrentPeriodIdx >= PF_MAX_NUM_TRACE_PERIODS) {
  1960. CCPF_ASSERT(FALSE);
  1961. Status = STATUS_INVALID_PARAMETER;
  1962. goto cleanup;
  1963. }
  1964. //
  1965. // Update the end for this period. It is beyond the
  1966. // current end by the number of entries logged in the
  1967. // period.
  1968. //
  1969. CurrentPeriodEndFaultIdx += RuntimeTrace->FaultsPerPeriod[CurrentPeriodIdx];
  1970. //
  1971. // This end fault index should not be greater than the
  1972. // total number of faults we logged.
  1973. //
  1974. if (CurrentPeriodEndFaultIdx > RuntimeTrace->NumFaults) {
  1975. CCPF_ASSERT(FALSE);
  1976. Status = STATUS_INVALID_PARAMETER;
  1977. goto cleanup;
  1978. }
  1979. }
  1980. //
  1981. // Make sure log entry's section id is within bounds.
  1982. //
  1983. if (LogEntry->SectionId >= RuntimeTrace->SectionTableSize) {
  1984. CCPF_ASSERT(FALSE);
  1985. continue;
  1986. }
  1987. NewSectionId = SectIdTranslationTable[LogEntry->SectionId];
  1988. //
  1989. // Copy only those entries for which we have a valid file
  1990. // name.
  1991. //
  1992. if (NewSectionId != CCPF_INVALID_TABLE_INDEX) {
  1993. //
  1994. // New section id should be within the number of sections in
  1995. // the final trace.
  1996. //
  1997. if ((USHORT) NewSectionId >= Trace->NumSections) {
  1998. CCPF_ASSERT(FALSE);
  1999. continue;
  2000. }
  2001. TargetLogEntry = &NewTraceEntries[NumEntriesCopied];
  2002. //
  2003. // Don't ever go beyond the buffer we had allocated.
  2004. //
  2005. if ((PCHAR) (TargetLogEntry + 1) > (PCHAR) Trace + TraceSize) {
  2006. CCPF_ASSERT(FALSE);
  2007. continue;
  2008. }
  2009. TargetLogEntry->FileOffset = LogEntry->FileOffset;
  2010. TargetLogEntry->SectionId = NewSectionId;
  2011. TargetLogEntry->IsImage = LogEntry->IsImage;
  2012. TargetLogEntry->InProcess = LogEntry->InProcess;
  2013. //
  2014. // Update number of entries copied for this period.
  2015. //
  2016. Trace->FaultsPerPeriod[CurrentPeriodIdx]++;
  2017. //
  2018. // Update the total number of entries copied.
  2019. //
  2020. NumEntriesCopied++;
  2021. }
  2022. }
  2023. }
  2024. Trace->NumEntries = NumEntriesCopied;
  2025. CCPF_ASSERT(Trace->NumEntries <= (ULONG) RuntimeTrace->NumFaults);
  2026. //
  2027. // Update destination pointer.
  2028. //
  2029. DestPtr += NumEntriesCopied * sizeof(PF_LOG_ENTRY);
  2030. //
  2031. // Add volume info structures. Clear the VolumeInfoOffset, so it
  2032. // will get set appropriately when we add the first volume.
  2033. //
  2034. Trace->VolumeInfoOffset = 0;
  2035. Trace->NumVolumes = 0;
  2036. Trace->VolumeInfoSize = 0;
  2037. HeadEntry = &RuntimeTrace->VolumeList;
  2038. NextEntry = HeadEntry->Flink;
  2039. while (NextEntry != HeadEntry) {
  2040. VolumeInfo = CONTAINING_RECORD(NextEntry,
  2041. CCPF_VOLUME_INFO,
  2042. VolumeLink);
  2043. NextEntry = NextEntry->Flink;
  2044. //
  2045. // Align the DestPtr for the VolumeInfo structure.
  2046. //
  2047. CCPF_ASSERT(PF_IS_POWER_OF_TWO(_alignof(PF_VOLUME_INFO)));
  2048. DestPtr = PF_ALIGN_UP(DestPtr, _alignof(PF_VOLUME_INFO));
  2049. //
  2050. // If this is the first VolumeInfo, update the offset in the
  2051. // trace header.
  2052. //
  2053. if (!Trace->VolumeInfoOffset) {
  2054. Trace->VolumeInfoOffset = (ULONG) (DestPtr - (PCHAR) Trace);
  2055. }
  2056. //
  2057. // Calculate size of this volume info in the dumped
  2058. // trace. Note that PF_VOLUME_INFO contains space for the
  2059. // terminating NUL.
  2060. //
  2061. VolumeInfoSize = sizeof(PF_VOLUME_INFO);
  2062. VolumeInfoSize += VolumeInfo->VolumePathLength * sizeof(WCHAR);
  2063. //
  2064. // Make sure we have space for this entry.
  2065. //
  2066. if (DestPtr + VolumeInfoSize > (PCHAR) Trace + TraceSize) {
  2067. CCPF_ASSERT(FALSE);
  2068. Status = STATUS_BUFFER_TOO_SMALL;
  2069. goto cleanup;
  2070. }
  2071. //
  2072. // Copy the data over.
  2073. //
  2074. TargetVolumeInfo = (PPF_VOLUME_INFO) DestPtr;
  2075. TargetVolumeInfo->CreationTime = VolumeInfo->CreationTime;
  2076. TargetVolumeInfo->SerialNumber = VolumeInfo->SerialNumber;
  2077. RtlCopyMemory(TargetVolumeInfo->VolumePath,
  2078. VolumeInfo->VolumePath,
  2079. (VolumeInfo->VolumePathLength + 1) * sizeof(WCHAR));
  2080. TargetVolumeInfo->VolumePathLength = VolumeInfo->VolumePathLength;
  2081. //
  2082. // Update DestPtr and the Trace header.
  2083. //
  2084. Trace->NumVolumes++;
  2085. DestPtr = DestPtr + VolumeInfoSize;
  2086. }
  2087. //
  2088. // Update VolumeInfoSize on the trace header.
  2089. //
  2090. Trace->VolumeInfoSize = (ULONG) (DestPtr - (PCHAR) Trace) - Trace->VolumeInfoOffset;
  2091. //
  2092. // Update trace header. We should not have copied more than what
  2093. // we allocated for.
  2094. //
  2095. Trace->Size = (ULONG) (DestPtr - (PCHAR) Trace);
  2096. CCPF_ASSERT(Trace->Size <= TraceSize);
  2097. //
  2098. // Make sure the trace we built passes the tests.
  2099. //
  2100. if (!PfVerifyTraceBuffer(Trace, Trace->Size, &FailedCheck)) {
  2101. CCPF_ASSERT(FALSE);
  2102. Status = STATUS_UNSUCCESSFUL;
  2103. goto cleanup;
  2104. }
  2105. Status = STATUS_SUCCESS;
  2106. cleanup:
  2107. if (SectIdTranslationTable) {
  2108. ExFreePool(SectIdTranslationTable);
  2109. }
  2110. if (!NT_SUCCESS(Status)) {
  2111. if (*TraceDump) {
  2112. ExFreePool(*TraceDump);
  2113. }
  2114. }
  2115. DBGPR((CCPFID,PFTRC,"CCPF: DumpTrace(%p)=%x [%d,%d]\n",
  2116. RuntimeTrace, Status, NumEntriesCopied, RuntimeTrace->NumFaults));
  2117. return Status;
  2118. }
  2119. VOID
  2120. CcPfCleanupTrace (
  2121. IN PCCPF_TRACE_HEADER Trace
  2122. )
  2123. /*++
  2124. Routine Description:
  2125. This routine cleans up allocated fields of a trace header, and
  2126. releases references. It does not free the trace structure
  2127. itself.
  2128. Arguments:
  2129. Trace - Trace to cleanup.
  2130. Return Value:
  2131. None.
  2132. Environment:
  2133. Kernel mode, IRQL == PASSIVE_LEVEL.
  2134. --*/
  2135. {
  2136. ULONG SectionIdx;
  2137. PCCPF_SECTION_INFO SectionInfo;
  2138. PCCPF_LOG_ENTRIES TraceBufferToFree;
  2139. PLIST_ENTRY ListHead;
  2140. PCCPF_VOLUME_INFO VolumeInfo;
  2141. DBGPR((CCPFID,PFTRC,"CCPF: CleanupTrace(%p)\n", Trace));
  2142. //
  2143. // Validate parameters.
  2144. //
  2145. CCPF_ASSERT(Trace && Trace->Magic == PF_TRACE_MAGIC_NUMBER);
  2146. //
  2147. // We should not have any sections we are still trying to get
  2148. // names for: we would have acquired a trace reference and cleanup
  2149. // functions would not get called with pending references.
  2150. //
  2151. CCPF_ASSERT(ExQueryDepthSList(&Trace->SectionsWithoutNamesList) == 0);
  2152. //
  2153. // Free the trace buffers.
  2154. //
  2155. while (!IsListEmpty(&Trace->TraceBuffersList)) {
  2156. ListHead = RemoveHeadList(&Trace->TraceBuffersList);
  2157. CCPF_ASSERT(Trace->NumTraceBuffers);
  2158. Trace->NumTraceBuffers--;
  2159. TraceBufferToFree = CONTAINING_RECORD(ListHead,
  2160. CCPF_LOG_ENTRIES,
  2161. TraceBuffersLink);
  2162. ExFreePool(TraceBufferToFree);
  2163. }
  2164. //
  2165. // Go through the section info hash. Free the file names and make
  2166. // sure we don't have any file objects referenced anymore.
  2167. //
  2168. if (Trace->SectionInfoTable) {
  2169. for (SectionIdx = 0; SectionIdx < Trace->SectionTableSize; SectionIdx++) {
  2170. SectionInfo = &Trace->SectionInfoTable[SectionIdx];
  2171. if (SectionInfo->EntryValid) {
  2172. if (SectionInfo->FileName) {
  2173. ExFreePool(SectionInfo->FileName);
  2174. }
  2175. if (SectionInfo->ReferencedFileObject) {
  2176. ObDereferenceObject(SectionInfo->ReferencedFileObject);
  2177. }
  2178. }
  2179. }
  2180. ExFreePool(Trace->SectionInfoTable);
  2181. }
  2182. //
  2183. // If there was a process we were associated with, release the
  2184. // reference we got on it.
  2185. //
  2186. if (Trace->Process) {
  2187. ObDereferenceObject(Trace->Process);
  2188. }
  2189. //
  2190. // Free the volume info nodes.
  2191. //
  2192. while (!IsListEmpty(&Trace->VolumeList)) {
  2193. CCPF_ASSERT(Trace->NumVolumes);
  2194. Trace->NumVolumes--;
  2195. ListHead = RemoveHeadList(&Trace->VolumeList);
  2196. VolumeInfo = CONTAINING_RECORD(ListHead,
  2197. CCPF_VOLUME_INFO,
  2198. VolumeLink);
  2199. ExFreePool(VolumeInfo);
  2200. }
  2201. }
  2202. VOID
  2203. CcPfTraceTimerRoutine(
  2204. IN PKDPC Dpc,
  2205. IN PVOID DeferredContext,
  2206. IN PVOID SystemArgument1,
  2207. IN PVOID SystemArgument2
  2208. )
  2209. /*++
  2210. Routine Description:
  2211. This routine is invoked as the DPC handler for the trace timer to
  2212. keep track of page faults per period as well as trace timeout.
  2213. Note that the timer may fire before the trace has been activated.
  2214. There is always a trace reference associated with the timer queued,
  2215. If the timer fires, this reference must be freed before this routine
  2216. returns. If the timer is canceled while in the queue, this reference
  2217. must be freed by who has canceled it.
  2218. Arguments:
  2219. DeferredContext - Pointer to the trace header.
  2220. Return Value:
  2221. None.
  2222. Environment:
  2223. Kernel mode. IRQL == DISPATCH_LEVEL.
  2224. --*/
  2225. {
  2226. PCCPF_TRACE_HEADER Trace;
  2227. NTSTATUS Status;
  2228. LONG NumFaults;
  2229. UNREFERENCED_PARAMETER (Dpc);
  2230. UNREFERENCED_PARAMETER (SystemArgument1);
  2231. UNREFERENCED_PARAMETER (SystemArgument2);
  2232. //
  2233. // Initialize locals.
  2234. //
  2235. Trace = DeferredContext;
  2236. DBGPR((CCPFID,PFTMR,"CCPF: TraceTimer(%p)\n", Trace));
  2237. //
  2238. // We already got a reference to our trace when the timer was queued.
  2239. // The fields we access / update in this routine are only accessed by
  2240. // the timer routine. There should be a single instance of this
  2241. // routine running on this trace.
  2242. //
  2243. CCPF_ASSERT(Trace && Trace->Magic == PF_TRACE_MAGIC_NUMBER);
  2244. //
  2245. // If the trace is going away don't do anything.
  2246. //
  2247. if (Trace->EndTraceCalled) {
  2248. Status = STATUS_TOO_LATE;
  2249. goto cleanup;
  2250. }
  2251. //
  2252. // Update number of faults for this period.
  2253. //
  2254. NumFaults = Trace->NumFaults;
  2255. //
  2256. // Don't let NumFaults be bigger than MaxFaults. We may interlocked increment
  2257. // then decrement Trace->NumFaults if it goes over MaxFaults.
  2258. //
  2259. if (NumFaults > Trace->MaxFaults) {
  2260. NumFaults = Trace->MaxFaults;
  2261. }
  2262. Trace->FaultsPerPeriod[Trace->CurPeriod] = NumFaults - Trace->LastNumFaults;
  2263. Trace->LastNumFaults = NumFaults;
  2264. //
  2265. // Update current period.
  2266. //
  2267. Trace->CurPeriod++;
  2268. //
  2269. // If current period is past max number of periods, try to queue
  2270. // end of trace work item.
  2271. //
  2272. if (Trace->CurPeriod >= PF_MAX_NUM_TRACE_PERIODS) {
  2273. //
  2274. // We should have caught CurPeriod before it goes above max.
  2275. //
  2276. CCPF_ASSERT(Trace->CurPeriod == PF_MAX_NUM_TRACE_PERIODS);
  2277. if (!InterlockedCompareExchange(&Trace->EndTraceCalled, 1, 0)) {
  2278. //
  2279. // We set EndTraceCalled from 0 to 1. We can queue the
  2280. // workitem now.
  2281. //
  2282. ExQueueWorkItem(&Trace->EndTraceWorkItem, DelayedWorkQueue);
  2283. }
  2284. } else {
  2285. //
  2286. // Queue ourselves for the next period.
  2287. //
  2288. KeAcquireSpinLockAtDpcLevel(&Trace->TraceTimerSpinLock);
  2289. if (!Trace->EndTraceCalled) {
  2290. //
  2291. // Requeue the timer only if the trace is not being ended.
  2292. //
  2293. Status = CcPfAddRef(&Trace->RefCount);
  2294. if (NT_SUCCESS(Status)) {
  2295. KeSetTimer(&Trace->TraceTimer,
  2296. Trace->TraceTimerPeriod,
  2297. &Trace->TraceTimerDpc);
  2298. }
  2299. }
  2300. KeReleaseSpinLockFromDpcLevel(&Trace->TraceTimerSpinLock);
  2301. //
  2302. // We should not touch any fields of the Trace beyond this point
  2303. // except releasing our reference count.
  2304. //
  2305. }
  2306. Status = STATUS_SUCCESS;
  2307. cleanup:
  2308. //
  2309. // Release the trace reference acquired when this timer was queued.
  2310. //
  2311. CcPfDecRef(&Trace->RefCount);
  2312. DBGPR((CCPFID,PFTMR,"CCPF: TraceTimer(%p)=%x\n", Trace, Status));
  2313. return;
  2314. }
  2315. NTSTATUS
  2316. CcPfCancelTraceTimer(
  2317. IN PCCPF_TRACE_HEADER Trace
  2318. )
  2319. /*++
  2320. Routine Description:
  2321. This function is called from CcPfEndTrace to cancel the timer and
  2322. release its refcount if it was in the queue.
  2323. It is a seperate function because it needs to acquire a spinlock and
  2324. CcPfEndTrace can remain pagable.
  2325. Arguments:
  2326. Trace - Pointer to trace header.
  2327. Return Value:
  2328. STATUS_SUCCESS.
  2329. Environment:
  2330. Kernel mode. IRQL == PASSIVE_LEVEL. Acquires spinlock.
  2331. --*/
  2332. {
  2333. KIRQL OrigIrql;
  2334. KeAcquireSpinLock(&Trace->TraceTimerSpinLock, &OrigIrql);
  2335. //
  2336. // We know that no new timers can be queued from here on because EndTraceCalled
  2337. // has been set and we have acquired the trace's timer lock. Running timer
  2338. // routines will release their references as they return.
  2339. //
  2340. if (KeCancelTimer(&Trace->TraceTimer)) {
  2341. //
  2342. // If we canceled a timer that was in the queue, then there was a reference
  2343. // associated with it. It is our responsibility to release it.
  2344. //
  2345. CcPfDecRef(&Trace->RefCount);
  2346. }
  2347. KeReleaseSpinLock(&Trace->TraceTimerSpinLock, OrigIrql);
  2348. return STATUS_SUCCESS;
  2349. }
  2350. VOID
  2351. CcPfEndTraceWorkerThreadRoutine(
  2352. PVOID Parameter
  2353. )
  2354. /*++
  2355. Routine Description:
  2356. This routine is queued to call end of trace function for the
  2357. specified trace.
  2358. Arguments:
  2359. Parameter - Pointer to trace to end.
  2360. Return Value:
  2361. None.
  2362. Environment:
  2363. Kernel mode. IRQL == PASSIVE_LEVEL.
  2364. --*/
  2365. {
  2366. PCCPF_TRACE_HEADER Trace;
  2367. //
  2368. // Initialize locals.
  2369. //
  2370. Trace = Parameter;
  2371. DBGPR((CCPFID,PFTRC,"CCPF: EndTraceWorker(%p)\n", Trace));
  2372. //
  2373. // Call the real end of trace routine.
  2374. //
  2375. CcPfEndTrace(Trace);
  2376. return;
  2377. }
  2378. VOID
  2379. CcPfGetFileNamesWorkerRoutine(
  2380. PVOID Parameter
  2381. )
  2382. /*++
  2383. Routine Description:
  2384. This routine is queued to get file names for sections we have
  2385. logged page faults to. GetFileNameWorkItemQueued on the trace
  2386. header should have been InterlockedCompareExchange'd from 0 to 1
  2387. and a reference to the trace should have been acquired before
  2388. this is queued. There are no locks protecting the trace's
  2389. SectionInfoTable, and this is how we make sure there is only one
  2390. routine trying to get filenames and update the table.
  2391. Note: This whole function is in a way a cleanup clause. We will
  2392. empty the SectionsWithoutNamesList queue, we get names or not. So
  2393. do not just put a return anywhere in the function without really
  2394. understanding the flow and making sure the list is cleaned up, so
  2395. all the file object references are deref'ed.
  2396. Arguments:
  2397. Parameter - Pointer to trace header.
  2398. Return Value:
  2399. None.
  2400. Environment:
  2401. Kernel mode. IRQL == PASSIVE_LEVEL. Uses interlocked slist operation.
  2402. --*/
  2403. {
  2404. PCCPF_TRACE_HEADER Trace;
  2405. PDEVICE_OBJECT DeviceObject;
  2406. POBJECT_NAME_INFORMATION FileNameInfo;
  2407. PFSRTL_COMMON_FCB_HEADER FcbHeader;
  2408. PWCHAR Suffix;
  2409. PWCHAR MFTFileSuffix;
  2410. ULONG QueryBufferSize;
  2411. ULONG ReturnedLength;
  2412. ULONG FileNameLength;
  2413. PCCPF_SECTION_INFO SectionInfo;
  2414. PSINGLE_LIST_ENTRY SectionLink;
  2415. ULONG NumNamesAcquired;
  2416. ULONG NumSectionsWithoutNames;
  2417. LONG NumPasses;
  2418. NTSTATUS Status;
  2419. LARGE_INTEGER WaitTimeout;
  2420. ULONG MFTFileSuffixLength;
  2421. LONG NumSleeps;
  2422. CSHORT NodeTypeCode;
  2423. //
  2424. // Initialize locals and validate parameters.
  2425. //
  2426. Trace = Parameter;
  2427. CCPF_ASSERT(Trace && Trace->Magic == PF_TRACE_MAGIC_NUMBER);
  2428. FileNameInfo = NULL;
  2429. NumNamesAcquired = 0;
  2430. NumSectionsWithoutNames = 0;
  2431. MFTFileSuffix = L"\\$Mft";
  2432. MFTFileSuffixLength = wcslen(MFTFileSuffix);
  2433. DBGPR((CCPFID,PFNAME,"CCPF: GetNames(%p)\n", Trace));
  2434. //
  2435. // Allocate a file name query buffer.
  2436. //
  2437. QueryBufferSize = sizeof(OBJECT_NAME_INFORMATION);
  2438. QueryBufferSize += PF_MAXIMUM_SECTION_FILE_NAME_LENGTH * sizeof(WCHAR);
  2439. FileNameInfo = ExAllocatePoolWithTag (PagedPool | POOL_COLD_ALLOCATION,
  2440. QueryBufferSize,
  2441. CCPF_ALLOC_QUERY_TAG);
  2442. if (!FileNameInfo) {
  2443. //
  2444. // We could not allocate a file name query buffer. Bummer, we
  2445. // still have to empty the queue, although we can't be getting
  2446. // any file names.
  2447. //
  2448. QueryBufferSize = 0;
  2449. DBGPR((CCPFID,PFWARN,"CCPF: GetNames-FailedQueryAlloc\n"));
  2450. }
  2451. NumPasses = 0;
  2452. NumSleeps = 0;
  2453. do {
  2454. //
  2455. // We may come back here if after saying that we (the get-name
  2456. // worker) are no longer active, and we see that there are
  2457. // still sections to get names for, and we reactivate
  2458. // ourselves. This covers the case when somebody decides not
  2459. // to start us because we are active, just as we are
  2460. // deactivating ourselves.
  2461. //
  2462. //
  2463. // While there are sections we have to get names for...
  2464. //
  2465. while (SectionLink = InterlockedPopEntrySList(&Trace->SectionsWithoutNamesList)) {
  2466. SectionInfo = CONTAINING_RECORD(SectionLink,
  2467. CCPF_SECTION_INFO,
  2468. GetNameLink);
  2469. NumSectionsWithoutNames++;
  2470. //
  2471. // We are getting names for sections. Clear the event that
  2472. // may have been signalled to tell us to do so.
  2473. //
  2474. KeClearEvent(&Trace->GetFileNameWorkerEvent);
  2475. //
  2476. // We should not have already gotten a file name for this
  2477. // valid section entry. We should have a referenced file
  2478. // object from which we can safely get a name, i.e. not a
  2479. // special file system object.
  2480. //
  2481. CCPF_ASSERT(SectionInfo->EntryValid);
  2482. CCPF_ASSERT(!SectionInfo->FileName);
  2483. CCPF_ASSERT(SectionInfo->ReferencedFileObject);
  2484. //
  2485. // If we could not allocate a file name query buffer, just skip this
  2486. // section. Note that we still had to dequeue it however.
  2487. //
  2488. if (!FileNameInfo) {
  2489. goto NextQueuedSection;
  2490. }
  2491. //
  2492. // Check if this pagefault is for a file that's on a fixed disk.
  2493. //
  2494. DeviceObject = IoGetRelatedDeviceObject(SectionInfo->ReferencedFileObject);
  2495. if ((DeviceObject == NULL) ||
  2496. (DeviceObject->DeviceType != FILE_DEVICE_DISK_FILE_SYSTEM) ||
  2497. (DeviceObject->Characteristics & (FILE_REMOVABLE_MEDIA | FILE_REMOTE_DEVICE))) {
  2498. //
  2499. // We will not get a section name for this section. This results
  2500. // in this section being ignored when preparing a trace dump.
  2501. //
  2502. goto NextQueuedSection;
  2503. }
  2504. //
  2505. // If this is a metafile section (e.g. for a directory) see if
  2506. // it is on a filesystem that supports metafile prefetching.
  2507. // A section is for internal file system metafile if its FsContext2
  2508. // is NULL.
  2509. //
  2510. if (SectionInfo->ReferencedFileObject->FsContext2 == 0) {
  2511. FcbHeader = SectionInfo->ReferencedFileObject->FsContext;
  2512. if (FcbHeader) {
  2513. //
  2514. // Currently only NTFS supports metafile prefetching. FAT hits
  2515. // a race condition if we ask names for metafile sections.
  2516. // To determine if it is for NTFS, we check the NodeType range
  2517. // on FsContext. 0x07xx is reserved for NTFS and 0x05xx
  2518. // is reserved for FAT.
  2519. NodeTypeCode = FcbHeader->NodeTypeCode;
  2520. if ((NodeTypeCode >> 8) != 0x07) {
  2521. //
  2522. // Skip this section.
  2523. //
  2524. goto NextQueuedSection;
  2525. }
  2526. //
  2527. // Note that this section is for metafile.
  2528. //
  2529. SectionInfo->Metafile = 1;
  2530. } else {
  2531. //
  2532. // We will not get a section name for this metafile section. This
  2533. // results in this section being ignored when preparing a trace dump.
  2534. //
  2535. goto NextQueuedSection;
  2536. }
  2537. }
  2538. //
  2539. // Try to get the name for the file object. This will most
  2540. // likely fail if we could not allocate a FileNameInfo
  2541. // buffer.
  2542. //
  2543. Status = ObQueryNameString(SectionInfo->ReferencedFileObject,
  2544. FileNameInfo,
  2545. QueryBufferSize,
  2546. &ReturnedLength);
  2547. if (!NT_SUCCESS(Status)) {
  2548. goto NextQueuedSection;
  2549. }
  2550. //
  2551. // Allocate a file name buffer and copy into
  2552. // it. The file names will be NUL terminated.
  2553. // Allocate extra for that.
  2554. //
  2555. FileNameLength = FileNameInfo->Name.Length / sizeof(WCHAR);
  2556. SectionInfo->FileName = ExAllocatePoolWithTag(PagedPool | POOL_COLD_ALLOCATION,
  2557. (FileNameLength + 1) * sizeof(WCHAR),
  2558. CCPF_ALLOC_FILENAME_TAG);
  2559. if (SectionInfo->FileName) {
  2560. RtlCopyMemory(SectionInfo->FileName,
  2561. FileNameInfo->Name.Buffer,
  2562. FileNameLength * sizeof(WCHAR));
  2563. //
  2564. // Make sure it is NUL terminated.
  2565. //
  2566. SectionInfo->FileName[FileNameLength] = 0;
  2567. //
  2568. // If the section is for a metafile check if it is for Mft.
  2569. // Unlike other metafile, we are interested in faults from
  2570. // Mft in addition to knowing that we accessed it at all.
  2571. //
  2572. if (SectionInfo->Metafile) {
  2573. if (FileNameLength >= MFTFileSuffixLength) {
  2574. Suffix = SectionInfo->FileName + FileNameLength;
  2575. Suffix -= MFTFileSuffixLength;
  2576. if (wcscmp(Suffix, MFTFileSuffix) == 0) {
  2577. //
  2578. // Clear the "Metafile" bit of MFT so we keep
  2579. // track of faults from it.
  2580. //
  2581. SectionInfo->Metafile = 0;
  2582. }
  2583. }
  2584. }
  2585. //
  2586. // Update the volume list with the volume this
  2587. // section is on. We reuse the existing query
  2588. // buffer to get volume's name since we've already
  2589. // copied the file's name to another buffer. The
  2590. // device object for the file should be for the
  2591. // volume.
  2592. //
  2593. Status = ObQueryNameString(SectionInfo->ReferencedFileObject->DeviceObject,
  2594. FileNameInfo,
  2595. QueryBufferSize,
  2596. &ReturnedLength);
  2597. if (NT_SUCCESS(Status)) {
  2598. RtlUpcaseUnicodeString(&FileNameInfo->Name, &FileNameInfo->Name, FALSE);
  2599. Status = CcPfUpdateVolumeList(Trace,
  2600. FileNameInfo->Name.Buffer,
  2601. FileNameInfo->Name.Length / sizeof(WCHAR));
  2602. }
  2603. if (!NT_SUCCESS(Status)) {
  2604. //
  2605. // If we could not update the volume list as
  2606. // necessary for this section, we have to
  2607. // cleanup and ignore this section.
  2608. //
  2609. ExFreePool(SectionInfo->FileName);
  2610. SectionInfo->FileName = NULL;
  2611. } else {
  2612. NumNamesAcquired++;
  2613. }
  2614. }
  2615. NextQueuedSection:
  2616. //
  2617. // Dereference the file object, and clear it on the section
  2618. // entry.
  2619. //
  2620. ObDereferenceObject(SectionInfo->ReferencedFileObject);
  2621. SectionInfo->ReferencedFileObject = NULL;
  2622. //
  2623. // If we could not get a name because the query failed or
  2624. // we could not allocate a name buffer, too bad. For this
  2625. // run, pagefaults for this section will be ignored. Over
  2626. // time it will straighten itself out.
  2627. //
  2628. }
  2629. //
  2630. // We don't seem to have any more queued section
  2631. // entries. Before marking ourself inactive, wait a
  2632. // little. Maybe someone will want us to get name for another
  2633. // section. Then we'll save the overhead of queuing another
  2634. // workitem. Set a limit on how long we'll wait though
  2635. // [negative because it is relative, in 100ns].
  2636. //
  2637. //
  2638. // Note that we are sleeping while holding a trace
  2639. // reference. If end trace gets called, it also signals the
  2640. // event to make us release that reference quicker.
  2641. //
  2642. //
  2643. // If we could not even allocate a query buffer,
  2644. // no reason to wait for more misery.
  2645. //
  2646. if (FileNameInfo) {
  2647. WaitTimeout.QuadPart = - 200 * 1000 * 10; // 200 ms.
  2648. DBGPR((CCPFID,PFNAMS,"CCPF: GetNames-Sleeping:%p\n", Trace));
  2649. NumSleeps++;
  2650. Status = KeWaitForSingleObject(&Trace->GetFileNameWorkerEvent,
  2651. Executive,
  2652. KernelMode,
  2653. FALSE,
  2654. &WaitTimeout);
  2655. DBGPR((CCPFID,PFNAMS,"CCPF: GetNames-WokeUp:%x\n", Status));
  2656. }
  2657. //
  2658. // If there are no new sections to get names for, go ahead and
  2659. // mark ourselves inactive, otherwise we will loop to get more
  2660. // names.
  2661. //
  2662. if (!ExQueryDepthSList(&Trace->SectionsWithoutNamesList)) {
  2663. //
  2664. // We went through all the queued section entries. Note that
  2665. // we are no longer active.
  2666. //
  2667. InterlockedExchange(&Trace->GetFileNameWorkItemQueued, 0);
  2668. //
  2669. // Check to see if there are new sections to get file
  2670. // names for since we last checked and marked ourselves
  2671. // inactive.
  2672. //
  2673. if (ExQueryDepthSList(&Trace->SectionsWithoutNamesList)) {
  2674. //
  2675. // Somebody may have inserted a section to get name for,
  2676. // but seeing us active may not have queued another work
  2677. // item. If it is so and we don't get name for that
  2678. // section, we may keep the file object referenced for
  2679. // longer than we'd like to. Try to mark ourselves active
  2680. // again.
  2681. //
  2682. if (!InterlockedCompareExchange(&Trace->GetFileNameWorkItemQueued,
  2683. 1,
  2684. 0)) {
  2685. //
  2686. // We marked ourselves active. They really may not
  2687. // have queued another worker. Loop and check for
  2688. // more work.
  2689. //
  2690. //
  2691. // Note that, they may not fool us to loop more
  2692. // than MaxSections through this path, since they
  2693. // have to still queue a new section to make the
  2694. // ExQueryDepthSList above return != 0, and there
  2695. // may be max MaxSections.
  2696. //
  2697. } else {
  2698. //
  2699. // It seems another worker was queued. Any items
  2700. // on the work list are that guy's problem
  2701. // now. Break out and cleanup.
  2702. //
  2703. break;
  2704. }
  2705. } else {
  2706. //
  2707. // No more work items on the list. We are really
  2708. // done. Just break out and cleanup.
  2709. //
  2710. break;
  2711. }
  2712. }
  2713. //
  2714. // Bump number of passes we've made over the sections-without-
  2715. // names-list. We should not have to make more passes than the
  2716. // max number of section info entries we can have. This is an
  2717. // infinite loop protection and should not happen. If it does,
  2718. // however, in the worst case we will keep a reference to a
  2719. // file object longer than we'd like to, and we may not get a
  2720. // file name for it.
  2721. //
  2722. NumPasses++;
  2723. if (NumPasses > Trace->MaxSections) {
  2724. CCPF_ASSERT(FALSE);
  2725. break;
  2726. }
  2727. } while (TRUE);
  2728. //
  2729. // Clean up:
  2730. //
  2731. if (FileNameInfo) {
  2732. ExFreePool(FileNameInfo);
  2733. }
  2734. //
  2735. // Release reference on the trace as the very last thing. Don't
  2736. // touch anything from the trace after this.
  2737. //
  2738. CcPfDecRef(&Trace->RefCount);
  2739. DBGPR((CCPFID,PFNAME,"CCPF: GetNames(%p)=%d-%d,[%d-%d]\n",
  2740. Trace, NumSectionsWithoutNames, NumNamesAcquired,
  2741. NumPasses, NumSleeps));
  2742. return;
  2743. }
  2744. LONG
  2745. CcPfLookUpSection(
  2746. PCCPF_SECTION_INFO Table,
  2747. ULONG TableSize,
  2748. PSECTION_OBJECT_POINTERS SectionObjectPointer,
  2749. PLONG AvailablePosition
  2750. )
  2751. /*++
  2752. Routine Description:
  2753. This routine is called to look up a section in the specified
  2754. section table hash. If the section is found its index is
  2755. returned. Otherwise index to where the section should go in the
  2756. table is put into AvailablePosition, if the table is not
  2757. full.
  2758. Arguments:
  2759. Table - An array of section info entries used as a hash table.
  2760. TableSize - Maximum size of the table.
  2761. SectionObjectPointer - This is used as a key to identify a mapping.
  2762. AvailablePosition - If section is not found and there is room in
  2763. the table, index of where the section should go is put here.
  2764. Return Value:
  2765. Index into the table where the section is found or CCPF_INVALID_TABLE_INDEX
  2766. Environment:
  2767. Kernel mode, IRQL <= DISPATCH_LEVEL if Table is NonPaged.
  2768. --*/
  2769. {
  2770. PCCPF_SECTION_INFO Entry;
  2771. ULONG StartIdx;
  2772. ULONG EndIdx;
  2773. ULONG EntryIdx;
  2774. ULONG HashIndex;
  2775. ULONG NumPasses;
  2776. //
  2777. // Get the hashed index into the table where the entry ideally
  2778. // should be at.
  2779. //
  2780. HashIndex = CcPfHashValue((PVOID)&SectionObjectPointer,
  2781. sizeof(SectionObjectPointer)) % TableSize;
  2782. //
  2783. // We will make two runs through the table looking for the
  2784. // entry. First starting from the hashed position up to the end of
  2785. // the table. Next from the beginning of the table up to the
  2786. // hashed position.
  2787. //
  2788. NumPasses = 0;
  2789. do {
  2790. //
  2791. // Setup start and end indices accordingly.
  2792. //
  2793. if (NumPasses == 0) {
  2794. StartIdx = HashIndex;
  2795. EndIdx = TableSize;
  2796. } else {
  2797. StartIdx = 0;
  2798. EndIdx = HashIndex;
  2799. }
  2800. for (EntryIdx = StartIdx; EntryIdx < EndIdx; EntryIdx++) {
  2801. Entry = &Table[EntryIdx];
  2802. if (Entry->EntryValid) {
  2803. if (Entry->SectionObjectPointer == SectionObjectPointer) {
  2804. //
  2805. // Check if other saved fields match the fields of
  2806. // the SectionObjectPointer we are trying to find.
  2807. // Please see the comments in CCPF_SECTION_INFO
  2808. // definition.
  2809. //
  2810. if (Entry->DataSectionObject == SectionObjectPointer->DataSectionObject &&
  2811. Entry->ImageSectionObject == SectionObjectPointer->ImageSectionObject) {
  2812. //
  2813. // We found the entry.
  2814. //
  2815. *AvailablePosition = CCPF_INVALID_TABLE_INDEX;
  2816. return EntryIdx;
  2817. } else if (Entry->DataSectionObject == SectionObjectPointer->DataSectionObject ||
  2818. Entry->ImageSectionObject == SectionObjectPointer->ImageSectionObject) {
  2819. //
  2820. // If one of them matches, check to see if the
  2821. // one that does not match is NULL on the
  2822. // Entry. We don't want to create two entries
  2823. // for the same file when it is first opened
  2824. // as data and then as image or vice
  2825. // versa. Note that if later image or data
  2826. // segment gets deleted, we may end up
  2827. // creating a new entry. We are optimizing
  2828. // only for the case that we think is likely
  2829. // to happen often.
  2830. //
  2831. if (Entry->DataSectionObject == NULL &&
  2832. SectionObjectPointer->DataSectionObject != NULL) {
  2833. DBGPR((CCPFID,PFLKUP,"CCPF: LookupSect-DataSectUpt(%p)\n", SectionObjectPointer));
  2834. //
  2835. // Try to update the entry. If our update
  2836. // was succesful, return found entry.
  2837. //
  2838. InterlockedCompareExchangePointer(&Entry->DataSectionObject,
  2839. SectionObjectPointer->DataSectionObject,
  2840. NULL);
  2841. if (Entry->DataSectionObject == SectionObjectPointer->DataSectionObject) {
  2842. *AvailablePosition = CCPF_INVALID_TABLE_INDEX;
  2843. return EntryIdx;
  2844. }
  2845. }
  2846. if (Entry->ImageSectionObject == NULL &&
  2847. SectionObjectPointer->ImageSectionObject != NULL) {
  2848. DBGPR((CCPFID,PFLKUP,"CCPF: LookupSect-ImgSectUpt(%p)\n", SectionObjectPointer));
  2849. //
  2850. // Try to update the entry. If our update
  2851. // was succesful, return found entry.
  2852. //
  2853. InterlockedCompareExchangePointer(&Entry->ImageSectionObject,
  2854. SectionObjectPointer->ImageSectionObject,
  2855. NULL);
  2856. if (Entry->ImageSectionObject == SectionObjectPointer->ImageSectionObject) {
  2857. *AvailablePosition = CCPF_INVALID_TABLE_INDEX;
  2858. return EntryIdx;
  2859. }
  2860. }
  2861. //
  2862. // Most likely, the field that matched was
  2863. // NULL, signifying nothing. Fall through to
  2864. // continue with the lookup.
  2865. //
  2866. }
  2867. //
  2868. // Although the SectionObjectPointer matches the
  2869. // other fields don't match. The old file may be
  2870. // gone and this may be a new file that somehow
  2871. // ended up with the same SectionObjectPointer.
  2872. // Continue the lookup.
  2873. //
  2874. }
  2875. } else {
  2876. //
  2877. // This is an available position. The fact that the entry
  2878. // is not here means the entry is not in the table.
  2879. //
  2880. *AvailablePosition = EntryIdx;
  2881. return CCPF_INVALID_TABLE_INDEX;
  2882. }
  2883. }
  2884. NumPasses++;
  2885. } while (NumPasses < 2);
  2886. //
  2887. // We could not find the entry or an available position.
  2888. //
  2889. *AvailablePosition = CCPF_INVALID_TABLE_INDEX;
  2890. return CCPF_INVALID_TABLE_INDEX;
  2891. }
  2892. NTSTATUS
  2893. CcPfGetCompletedTrace (
  2894. PVOID Buffer,
  2895. ULONG BufferSize,
  2896. PULONG ReturnSize
  2897. )
  2898. /*++
  2899. Routine Description:
  2900. If there is a completed scenario trace on the completed traces
  2901. list, this routine tries to copy it into the supplied buffer and
  2902. remove it. If BufferSize is too small, nothing is copied or
  2903. removed from the list, but ReturnSize is set to how big a buffer
  2904. is needed to get the first trace on the list. If BufferSize is
  2905. large enough, the number of bytes copied into the buffer is set on
  2906. the ReturnSize.
  2907. Arguments:
  2908. Buffer - Caller supplied buffer to copy a completed trace into.
  2909. BufferSize - Size of the caller supplied buffer in bytes.
  2910. ReturnSize - If BufferSize is big enough for the completed trace
  2911. number of bytes copied is put here. If BufferSize is not big
  2912. enough for the trace, the required size is put here. If there
  2913. are no more entries, this variable undefined.
  2914. Return Value:
  2915. STATUS_BUFFER_TOO_SMALL - BufferSize is not big enough for the
  2916. first completed trace on the list.
  2917. STATUS_NO_MORE_ENTRIES - There are no more completed traces on the
  2918. list.
  2919. STATUS_SUCCESS - A trace was removed from the list and copied into
  2920. the buffer.
  2921. or other status.
  2922. Environment:
  2923. Kernel mode, IRQL == PASSIVE_LEVEL.
  2924. --*/
  2925. {
  2926. PCCPF_TRACE_DUMP TraceDump;
  2927. NTSTATUS Status;
  2928. KPROCESSOR_MODE PreviousMode;
  2929. BOOLEAN HoldingCompletedTracesLock;
  2930. //
  2931. // Initialize locals.
  2932. //
  2933. HoldingCompletedTracesLock = FALSE;
  2934. DBGPR((CCPFID,PFTRC,"CCPF: GetCompletedTrace()\n"));
  2935. //
  2936. // Get the completed traces lock.
  2937. //
  2938. ExAcquireFastMutex(&CcPfGlobals.CompletedTracesLock);
  2939. HoldingCompletedTracesLock = TRUE;
  2940. //
  2941. // If the list is empty, there are no more completed trace entries.
  2942. //
  2943. if (IsListEmpty(&CcPfGlobals.CompletedTraces)) {
  2944. Status = STATUS_NO_MORE_ENTRIES;
  2945. goto cleanup;
  2946. }
  2947. //
  2948. // Peek at the trace to see if it will fit into the supplied
  2949. // buffer.
  2950. //
  2951. TraceDump = CONTAINING_RECORD(CcPfGlobals.CompletedTraces.Flink,
  2952. CCPF_TRACE_DUMP,
  2953. CompletedTracesLink);
  2954. if (TraceDump->Trace.Size > BufferSize) {
  2955. *ReturnSize = TraceDump->Trace.Size;
  2956. Status = STATUS_BUFFER_TOO_SMALL;
  2957. goto cleanup;
  2958. }
  2959. //
  2960. // The trace will fit in the user supplied buffer. Remove it from
  2961. // the list, release the lock and copy it.
  2962. //
  2963. RemoveHeadList(&CcPfGlobals.CompletedTraces);
  2964. CcPfGlobals.NumCompletedTraces--;
  2965. ExReleaseFastMutex(&CcPfGlobals.CompletedTracesLock);
  2966. HoldingCompletedTracesLock = FALSE;
  2967. //
  2968. // Copy the completed trace buffer.
  2969. //
  2970. Status = STATUS_SUCCESS;
  2971. try {
  2972. //
  2973. // If called from user-mode, probe whether it is safe to write
  2974. // to the pointer passed in.
  2975. //
  2976. PreviousMode = KeGetPreviousMode();
  2977. if (PreviousMode != KernelMode) {
  2978. ProbeForWrite(Buffer, BufferSize, _alignof(PF_TRACE_HEADER));
  2979. }
  2980. //
  2981. // Copy into the probed user buffer.
  2982. //
  2983. RtlCopyMemory(Buffer,
  2984. &TraceDump->Trace,
  2985. TraceDump->Trace.Size);
  2986. } except (EXCEPTION_EXECUTE_HANDLER) {
  2987. Status = GetExceptionCode();
  2988. }
  2989. if (!NT_SUCCESS(Status)) {
  2990. //
  2991. // The copy failed. Requeue the trace for the next query.
  2992. // Note that we might end up with one too many traces in
  2993. // the list because of this, but that's OK.
  2994. //
  2995. ExAcquireFastMutex(&CcPfGlobals.CompletedTracesLock);
  2996. HoldingCompletedTracesLock = TRUE;
  2997. InsertHeadList(&CcPfGlobals.CompletedTraces,&TraceDump->CompletedTracesLink);
  2998. CcPfGlobals.NumCompletedTraces++;
  2999. } else {
  3000. //
  3001. // Set number of bytes copied.
  3002. //
  3003. *ReturnSize = TraceDump->Trace.Size;
  3004. //
  3005. // Free the trace dump entry.
  3006. //
  3007. ExFreePool(TraceDump);
  3008. //
  3009. // We are done.
  3010. //
  3011. Status = STATUS_SUCCESS;
  3012. }
  3013. cleanup:
  3014. if (HoldingCompletedTracesLock) {
  3015. ExReleaseFastMutex(&CcPfGlobals.CompletedTracesLock);
  3016. }
  3017. DBGPR((CCPFID,PFTRC,"CCPF: GetCompletedTrace()=%x\n", Status));
  3018. return Status;
  3019. }
  3020. NTSTATUS
  3021. CcPfUpdateVolumeList(
  3022. PCCPF_TRACE_HEADER Trace,
  3023. WCHAR *VolumePath,
  3024. ULONG VolumePathLength
  3025. )
  3026. /*++
  3027. Routine Description:
  3028. If the specified volume is not in the volume list of Trace, its
  3029. information is acquired and added to the list.
  3030. This routine does not use any synchronization when accessing and
  3031. updating the volume list on the trace.
  3032. Arguments:
  3033. Trace - Pointer to trace.
  3034. VolumePath - Pointer to UPCASED volume path. Does NOT need to be NUL
  3035. terminated.
  3036. VolumePathLength - Length of VolumePath in characters excluding
  3037. NUL.
  3038. Return Value:
  3039. Status.
  3040. Environment:
  3041. Kernel mode. IRQL == PASSIVE_LEVEL.
  3042. --*/
  3043. {
  3044. NTSTATUS Status;
  3045. PLIST_ENTRY NextEntry;
  3046. PLIST_ENTRY FoundPosition;
  3047. PLIST_ENTRY HeadEntry;
  3048. PCCPF_VOLUME_INFO CurrentVolumeInfo;
  3049. PCCPF_VOLUME_INFO NewVolumeInfo;
  3050. LONG ComparisonResult;
  3051. ULONG AllocationSize;
  3052. BOOLEAN InsertedNewVolume;
  3053. //
  3054. // Define an enumeration for the passes we make over the volume
  3055. // list.
  3056. //
  3057. enum {
  3058. LookingForVolume,
  3059. AddingNewVolume,
  3060. MaxLoopIdx
  3061. } LoopIdx;
  3062. //
  3063. // Initialize locals.
  3064. //
  3065. NewVolumeInfo = NULL;
  3066. InsertedNewVolume = FALSE;
  3067. //
  3068. // We should be called with a valid volume name.
  3069. //
  3070. if (!VolumePathLength) {
  3071. CCPF_ASSERT(VolumePathLength != 0);
  3072. Status = STATUS_INVALID_PARAMETER;
  3073. goto cleanup;
  3074. }
  3075. //
  3076. // Walk the volume list. We will make two passes. First we will
  3077. // check to see if the volume already exists in the list. If it
  3078. // does not, we'll release the lock, build a new volume node and
  3079. // make a second pass to insert it.
  3080. //
  3081. for (LoopIdx = LookingForVolume; LoopIdx < MaxLoopIdx; LoopIdx++) {
  3082. //
  3083. // Determine what to do based on which pass we are in.
  3084. //
  3085. if (LoopIdx == LookingForVolume) {
  3086. CCPF_ASSERT(!InsertedNewVolume);
  3087. CCPF_ASSERT(!NewVolumeInfo);
  3088. } else if (LoopIdx == AddingNewVolume) {
  3089. CCPF_ASSERT(!InsertedNewVolume);
  3090. CCPF_ASSERT(NewVolumeInfo);
  3091. } else {
  3092. //
  3093. // We should only loop two times.
  3094. //
  3095. CCPF_ASSERT(FALSE);
  3096. Status = STATUS_UNSUCCESSFUL;
  3097. goto cleanup;
  3098. }
  3099. HeadEntry = &Trace->VolumeList;
  3100. NextEntry = HeadEntry->Flink;
  3101. FoundPosition = NULL;
  3102. while (NextEntry != HeadEntry) {
  3103. CurrentVolumeInfo = CONTAINING_RECORD(NextEntry,
  3104. CCPF_VOLUME_INFO,
  3105. VolumeLink);
  3106. NextEntry = NextEntry->Flink;
  3107. ComparisonResult = wcsncmp(VolumePath,
  3108. CurrentVolumeInfo->VolumePath,
  3109. VolumePathLength);
  3110. if (ComparisonResult == 0) {
  3111. //
  3112. // Make sure VolumePathLength's are equal
  3113. //
  3114. if (CurrentVolumeInfo->VolumePathLength != VolumePathLength) {
  3115. //
  3116. // Continue searching.
  3117. //
  3118. continue;
  3119. }
  3120. //
  3121. // The volume already exists in the list.
  3122. //
  3123. Status = STATUS_SUCCESS;
  3124. goto cleanup;
  3125. } else if (ComparisonResult < 0) {
  3126. //
  3127. // The volume paths are sorted lexically. The file
  3128. // path would be less than other volumes too. We'd
  3129. // insert the new node before this entry.
  3130. //
  3131. FoundPosition = &CurrentVolumeInfo->VolumeLink;
  3132. break;
  3133. }
  3134. //
  3135. // Continue looking...
  3136. //
  3137. }
  3138. //
  3139. // If we could not find an entry to insert the new node
  3140. // before, it goes before the list head.
  3141. //
  3142. if (!FoundPosition) {
  3143. FoundPosition = HeadEntry;
  3144. }
  3145. //
  3146. // If we come here, we could not find the volume in the list.
  3147. //
  3148. //
  3149. // If this is the first pass over the list (we were checking
  3150. // if the volume already exists), release the lock and build a
  3151. // volume node.
  3152. //
  3153. if (LoopIdx == LookingForVolume) {
  3154. //
  3155. // Build a new node. Note that CCPF_VOLUME_INFO already
  3156. // has space for the terminating NUL character.
  3157. //
  3158. AllocationSize = sizeof(CCPF_VOLUME_INFO);
  3159. AllocationSize += VolumePathLength * sizeof(WCHAR);
  3160. NewVolumeInfo = ExAllocatePoolWithTag(PagedPool | POOL_COLD_ALLOCATION,
  3161. AllocationSize,
  3162. CCPF_ALLOC_VOLUME_TAG);
  3163. if (!NewVolumeInfo) {
  3164. Status = STATUS_INSUFFICIENT_RESOURCES;
  3165. goto cleanup;
  3166. }
  3167. //
  3168. // Copy the volume name and terminate it.
  3169. //
  3170. RtlCopyMemory(NewVolumeInfo->VolumePath,
  3171. VolumePath,
  3172. VolumePathLength * sizeof(WCHAR));
  3173. NewVolumeInfo->VolumePath[VolumePathLength] = 0;
  3174. NewVolumeInfo->VolumePathLength = VolumePathLength;
  3175. //
  3176. // Query the signature and creation time.
  3177. //
  3178. Status = CcPfQueryVolumeInfo(NewVolumeInfo->VolumePath,
  3179. NULL,
  3180. &NewVolumeInfo->CreationTime,
  3181. &NewVolumeInfo->SerialNumber);
  3182. if (!NT_SUCCESS(Status)) {
  3183. goto cleanup;
  3184. }
  3185. //
  3186. // The new volume is ready to be inserted into the list,
  3187. // if somebody has not acted before us. Loop and go
  3188. // through the volume list again.
  3189. //
  3190. } else if (LoopIdx == AddingNewVolume) {
  3191. //
  3192. // Insert the volume node before the found position.
  3193. //
  3194. InsertTailList(FoundPosition, &NewVolumeInfo->VolumeLink);
  3195. Trace->NumVolumes++;
  3196. InsertedNewVolume = TRUE;
  3197. Status = STATUS_SUCCESS;
  3198. goto cleanup;
  3199. } else {
  3200. //
  3201. // We should only loop two times.
  3202. //
  3203. CCPF_ASSERT(FALSE);
  3204. Status = STATUS_UNSUCCESSFUL;
  3205. goto cleanup;
  3206. }
  3207. }
  3208. //
  3209. // We should not come here.
  3210. //
  3211. CCPF_ASSERT(FALSE);
  3212. Status = STATUS_UNSUCCESSFUL;
  3213. cleanup:
  3214. if (!NT_SUCCESS(Status)) {
  3215. if (NewVolumeInfo) {
  3216. ExFreePool(NewVolumeInfo);
  3217. }
  3218. } else {
  3219. if (!InsertedNewVolume && NewVolumeInfo) {
  3220. ExFreePool(NewVolumeInfo);
  3221. }
  3222. }
  3223. return Status;
  3224. }
  3225. //
  3226. // Routines used for prefetching and dealing with prefetch instructions.
  3227. //
  3228. NTSTATUS
  3229. CcPfPrefetchScenario (
  3230. PPF_SCENARIO_HEADER Scenario
  3231. )
  3232. /*++
  3233. Routine Description:
  3234. This routine checks for prefetch instructions for the specified
  3235. scenario and asks Mm to prefetch those pages.
  3236. Arguments:
  3237. Scenario - Prefetch instructions for the scenario.
  3238. Return Value:
  3239. Status.
  3240. Environment:
  3241. Kernel mode, IRQL == PASSIVE_LEVEL.
  3242. --*/
  3243. {
  3244. NTSTATUS Status;
  3245. CCPF_PREFETCH_HEADER PrefetchHeader;
  3246. //
  3247. // Initialize locals & prefetch context.
  3248. //
  3249. CcPfInitializePrefetchHeader(&PrefetchHeader);
  3250. DBGPR((CCPFID,PFPREF,"CCPF: PrefetchScenario(%p)\n", Scenario));
  3251. //
  3252. // Scenario instructions should be passed in.
  3253. //
  3254. if (!Scenario) {
  3255. CCPF_ASSERT(Scenario);
  3256. Status = STATUS_INVALID_PARAMETER;
  3257. goto cleanup;
  3258. }
  3259. //
  3260. // Check if prefetching is enabled.
  3261. //
  3262. if (!CCPF_IS_PREFETCHER_ENABLED()) {
  3263. Status = STATUS_NOT_SUPPORTED;
  3264. goto cleanup;
  3265. }
  3266. //
  3267. // Check if prefetching is enabled for the specified scenario type.
  3268. //
  3269. if (CcPfGlobals.Parameters.Parameters.EnableStatus[Scenario->ScenarioType] != PfSvEnabled) {
  3270. Status = STATUS_NOT_SUPPORTED;
  3271. goto cleanup;
  3272. }
  3273. //
  3274. // Save prefetch instructions pointer on the header.
  3275. //
  3276. PrefetchHeader.Scenario = Scenario;
  3277. //
  3278. // Try to make sure we have enough available memory to prefetch
  3279. // what we want to prefetch.
  3280. //
  3281. if (!MmIsMemoryAvailable(PrefetchHeader.Scenario->NumPages)) {
  3282. Status = STATUS_INSUFFICIENT_RESOURCES;
  3283. DBGPR((CCPFID,PFPREF,"CCPF: PrefetchScenario-MemNotAvailable\n"));
  3284. goto cleanup;
  3285. }
  3286. //
  3287. // Open the volumes we will prefetch on, making sure they are
  3288. // already mounted and the serials match etc.
  3289. //
  3290. Status = CcPfOpenVolumesForPrefetch(&PrefetchHeader);
  3291. if (!NT_SUCCESS(Status)) {
  3292. goto cleanup;
  3293. }
  3294. //
  3295. // Prefetch the filesystem metadata we will need, so metadata I/Os
  3296. // do not get in the way of efficient prefetch I/O. Since this is
  3297. // not critical, ignore return value.
  3298. //
  3299. CcPfPrefetchMetadata(&PrefetchHeader);
  3300. //
  3301. // Prefetch the pages accessed through data mappings. This will
  3302. // also bring in the header pages for image mappings.
  3303. //
  3304. Status = CcPfPrefetchSections(&PrefetchHeader,
  3305. CcPfPrefetchAllDataPages,
  3306. NULL,
  3307. 0,
  3308. NULL,
  3309. NULL);
  3310. if (!NT_SUCCESS(Status)) {
  3311. goto cleanup;
  3312. }
  3313. //
  3314. // Prefetch the pages accessed through image mappings.
  3315. //
  3316. Status = CcPfPrefetchSections(&PrefetchHeader,
  3317. CcPfPrefetchAllImagePages,
  3318. NULL,
  3319. 0,
  3320. NULL,
  3321. NULL);
  3322. if (!NT_SUCCESS(Status)) {
  3323. goto cleanup;
  3324. }
  3325. Status = STATUS_SUCCESS;
  3326. cleanup:
  3327. CcPfCleanupPrefetchHeader(&PrefetchHeader);
  3328. DBGPR((CCPFID,PFPREF,"CCPF: PrefetchScenario(%ws)=%x\n", Scenario->ScenarioId.ScenName, Status));
  3329. return Status;
  3330. }
  3331. NTSTATUS
  3332. CcPfPrefetchSections(
  3333. IN PCCPF_PREFETCH_HEADER PrefetchHeader,
  3334. IN CCPF_PREFETCH_TYPE PrefetchType,
  3335. OPTIONAL IN PCCPF_PREFETCH_CURSOR StartCursor,
  3336. OPTIONAL ULONG TotalPagesToPrefetch,
  3337. OPTIONAL OUT PULONG NumPagesPrefetched,
  3338. OPTIONAL OUT PCCPF_PREFETCH_CURSOR EndCursor
  3339. )
  3340. /*++
  3341. Routine Description:
  3342. This routine prepares read lists for the specified pages in the
  3343. scenario and calls Mm to prefetch them. This function is usually
  3344. called first to prefetch data pages then image pages. When
  3345. prefetching data pages, header pages for any image mappings are
  3346. also prefetched, which would otherwise hurt efficiency when
  3347. prefetching image pages.
  3348. Arguments:
  3349. PrefetchHeader - Pointer to the prefetch header.
  3350. PrefetchType - What/How to prefetch.
  3351. StartCursor - If prefetching only part of the scenario, where to
  3352. start prefetching from.
  3353. TotalPagesToPrefetch - If prefetching only part of the scenario, how
  3354. many pages to prefetch. This function may prefetch more or less pages
  3355. as it sees fit.
  3356. NumPagesPrefetched - If prefetching only part of the scenario,
  3357. this is the number of pages we asked Mm to prefetch.
  3358. EndCursor - If prefetching only part of the scenario, this is
  3359. updated to the position NumPages pages after the StartCursor.
  3360. Return Value:
  3361. Status.
  3362. Environment:
  3363. Kernel mode. IRQL == PASSIVE_LEVEL.
  3364. --*/
  3365. {
  3366. PWCHAR FilePath;
  3367. PCCPF_PREFETCH_VOLUME_INFO VolumeNode;
  3368. PREAD_LIST *ReadLists;
  3369. PREAD_LIST ReadList;
  3370. HANDLE *FileHandleTable;
  3371. HANDLE FileHandle;
  3372. PFILE_OBJECT *FileObjectTable;
  3373. PFILE_OBJECT FileObject;
  3374. PSECTION *SectionObjectTable;
  3375. PSECTION SectionObject;
  3376. PPF_SECTION_RECORD SectionRecord;
  3377. PPF_SECTION_RECORD SectionRecords;
  3378. PCHAR FileNameData;
  3379. UNICODE_STRING SectionName;
  3380. PPF_PAGE_RECORD PageRecord;
  3381. PPF_PAGE_RECORD PageRecords;
  3382. ULONG SectionIdx;
  3383. ULONG ReadListIdx;
  3384. LONG PageIdx;
  3385. LONG PreviousPageIdx;
  3386. ULONG NumReadLists;
  3387. ULONG AllocationSize;
  3388. NTSTATUS Status;
  3389. LOGICAL PrefetchingImagePages;
  3390. BOOLEAN AddedHeaderPage;
  3391. BOOLEAN PrefetchingPartOfScenario;
  3392. ULONGLONG LastOffset;
  3393. ULONG NumberOfSections;
  3394. ULONG NumPagesToPrefetch;
  3395. ULONG NumSectionPages;
  3396. PUCHAR Tables;
  3397. PUCHAR CurrentPosition;
  3398. PPF_SCENARIO_HEADER Scenario;
  3399. ULONG StartSectionNumber;
  3400. ULONG StartPageNumber;
  3401. //
  3402. // Initialize locals so we know what to cleanup.
  3403. //
  3404. Scenario = PrefetchHeader->Scenario;
  3405. Tables = NULL;
  3406. ReadList = NULL;
  3407. ReadLists = NULL;
  3408. FileHandle = NULL;
  3409. FileHandleTable = NULL;
  3410. FileObject = NULL;
  3411. FileObjectTable = NULL;
  3412. SectionObject = NULL;
  3413. SectionObjectTable = NULL;
  3414. NumReadLists = 0;
  3415. NumberOfSections = Scenario->NumSections;
  3416. NumPagesToPrefetch = 0;
  3417. NumSectionPages = 0;
  3418. PageIdx = 0;
  3419. DBGPR((CCPFID,PFPREF,"CCPF: PrefetchSections(%p,%d,%d,%d)\n",
  3420. PrefetchHeader, PrefetchType,
  3421. (StartCursor)?StartCursor->SectionIdx:0,
  3422. (StartCursor)?StartCursor->PageIdx:0));
  3423. //
  3424. // Validate parameters.
  3425. //
  3426. if (PrefetchType >= CcPfMaxPrefetchType) {
  3427. Status = STATUS_INVALID_PARAMETER;
  3428. goto cleanup;
  3429. }
  3430. //
  3431. // Determine whether we are prefetching data or image pages and
  3432. // other parameters based on prefetch type.
  3433. //
  3434. switch (PrefetchType) {
  3435. case CcPfPrefetchAllDataPages:
  3436. StartSectionNumber = 0;
  3437. StartPageNumber = 0;
  3438. PrefetchingImagePages = FALSE;
  3439. PrefetchingPartOfScenario = FALSE;
  3440. break;
  3441. case CcPfPrefetchAllImagePages:
  3442. StartSectionNumber = 0;
  3443. StartPageNumber = 0;
  3444. PrefetchingImagePages = TRUE;
  3445. PrefetchingPartOfScenario = FALSE;
  3446. break;
  3447. case CcPfPrefetchPartOfDataPages:
  3448. if (!StartCursor) {
  3449. CCPF_ASSERT(StartCursor);
  3450. Status = STATUS_INVALID_PARAMETER;
  3451. goto cleanup;
  3452. }
  3453. StartSectionNumber = StartCursor->SectionIdx;
  3454. StartPageNumber = StartCursor->PageIdx;
  3455. PrefetchingImagePages = FALSE;
  3456. PrefetchingPartOfScenario = TRUE;
  3457. break;
  3458. case CcPfPrefetchPartOfImagePages:
  3459. if (!StartCursor) {
  3460. CCPF_ASSERT(StartCursor);
  3461. Status = STATUS_INVALID_PARAMETER;
  3462. goto cleanup;
  3463. }
  3464. StartSectionNumber = StartCursor->SectionIdx;
  3465. StartPageNumber = StartCursor->PageIdx;
  3466. PrefetchingImagePages = TRUE;
  3467. PrefetchingPartOfScenario = TRUE;
  3468. break;
  3469. default:
  3470. //
  3471. // We should be handling all types above.
  3472. //
  3473. CCPF_ASSERT(FALSE);
  3474. Status = STATUS_INVALID_PARAMETER;
  3475. goto cleanup;
  3476. }
  3477. //
  3478. // Allocate and initialize intermediate tables. We will make a
  3479. // single allocation for all the tables.
  3480. //
  3481. AllocationSize = sizeof(PREAD_LIST) * NumberOfSections;
  3482. AllocationSize += sizeof(HANDLE) * NumberOfSections;
  3483. AllocationSize += sizeof(PFILE_OBJECT) * NumberOfSections;
  3484. AllocationSize += sizeof(PSECTION) * NumberOfSections;
  3485. Tables = ExAllocatePoolWithTag(PagedPool,
  3486. AllocationSize,
  3487. CCPF_ALLOC_INTRTABL_TAG);
  3488. if (!Tables) {
  3489. Status = STATUS_INSUFFICIENT_RESOURCES;
  3490. goto cleanup;
  3491. }
  3492. //
  3493. // Zero out the whole buffer. This initializes all elements of the
  3494. // tables to NULL.
  3495. //
  3496. RtlZeroMemory(Tables, AllocationSize);
  3497. //
  3498. // Determine where each table goes in the buffer.
  3499. //
  3500. CurrentPosition = Tables;
  3501. ReadLists = (PREAD_LIST *) CurrentPosition;
  3502. CurrentPosition += sizeof(PREAD_LIST) * NumberOfSections;
  3503. FileHandleTable = (HANDLE *) CurrentPosition;
  3504. CurrentPosition += sizeof(HANDLE) * NumberOfSections;
  3505. FileObjectTable = (PFILE_OBJECT *) CurrentPosition;
  3506. CurrentPosition += sizeof(PFILE_OBJECT) * NumberOfSections;
  3507. SectionObjectTable = (PSECTION *) CurrentPosition;
  3508. CurrentPosition += sizeof(PSECTION) * NumberOfSections;
  3509. //
  3510. // We should have allocated the right size buffer.
  3511. //
  3512. CCPF_ASSERT(CurrentPosition == Tables + AllocationSize);
  3513. //
  3514. // Go through the sections and prepare read lists. We may not have
  3515. // a read list for every section in the scenario so keep another
  3516. // counter, NumReadLists, to keep our read list array compact.
  3517. //
  3518. SectionRecords = (PPF_SECTION_RECORD)
  3519. ((PCHAR) Scenario + Scenario->SectionInfoOffset);
  3520. PageRecords = (PPF_PAGE_RECORD)
  3521. ((PCHAR) Scenario + Scenario->PageInfoOffset);
  3522. FileNameData = (PCHAR) Scenario + Scenario->FileNameInfoOffset;
  3523. for (SectionIdx = StartSectionNumber;
  3524. SectionIdx < NumberOfSections;
  3525. SectionIdx ++) {
  3526. SectionRecord = &SectionRecords[SectionIdx];
  3527. //
  3528. // Skip this section if it was marked ignore for some reason.
  3529. //
  3530. if (SectionRecord->IsIgnore) {
  3531. continue;
  3532. }
  3533. //
  3534. // If this section is on a bad volume (e.g. one that was not
  3535. // mounted or whose serial / creation time did not match the
  3536. // volume we had traced), we cannot prefetch this section.
  3537. //
  3538. FilePath = (WCHAR *) (FileNameData + SectionRecord->FileNameOffset);
  3539. VolumeNode = CcPfFindPrefetchVolumeInfoInList(FilePath,
  3540. &PrefetchHeader->BadVolumeList);
  3541. if (VolumeNode) {
  3542. continue;
  3543. }
  3544. //
  3545. // The section info should either be for an image or data
  3546. // mapping or both.
  3547. //
  3548. CCPF_ASSERT(SectionRecord->IsImage || SectionRecord->IsData);
  3549. //
  3550. // If we are mapping images and this section does not have an
  3551. // image mapping skip it. Note that the reverse is not
  3552. // true. We prefetch headers for isimage section when
  3553. // prefedata pages so we don't check for that.
  3554. //
  3555. if (PrefetchingImagePages && !SectionRecord->IsImage) {
  3556. continue;
  3557. }
  3558. //
  3559. // Allocate a read list. Note that READ_LIST has storage for a
  3560. // FILE_SEGMENT_ELEMENT. We allocate space for one extra page
  3561. // in case we have to also bring in a header page for image
  3562. // mapping.
  3563. //
  3564. AllocationSize = sizeof(READ_LIST) +
  3565. (SectionRecord->NumPages * sizeof(FILE_SEGMENT_ELEMENT));
  3566. ReadList = ExAllocatePoolWithTag(NonPagedPool,
  3567. AllocationSize,
  3568. CCPF_ALLOC_READLIST_TAG);
  3569. if (ReadList == NULL) {
  3570. Status = STATUS_INSUFFICIENT_RESOURCES;
  3571. goto cleanup;
  3572. }
  3573. //
  3574. // Initialize header fields of the read list.
  3575. //
  3576. ReadList->FileObject = 0;
  3577. ReadList->IsImage = PrefetchingImagePages;
  3578. ReadList->NumberOfEntries = 0;
  3579. //
  3580. // If we are prefetching data pages and this section was
  3581. // mapped as an image, add the header page to the readlist.
  3582. // This way when creating the image mapping to prefetch image
  3583. // pages we don't have to read it from the disk inefficiently.
  3584. //
  3585. AddedHeaderPage = FALSE;
  3586. if((PrefetchingImagePages == FALSE) && SectionRecord->IsImage) {
  3587. //
  3588. // Don't add the header page if we are prefetching only
  3589. // part of the section and we are past the first page.
  3590. //
  3591. if (!PrefetchingPartOfScenario ||
  3592. (StartSectionNumber != SectionIdx) ||
  3593. StartPageNumber > 0) {
  3594. //
  3595. // Header page starts at offset 0.
  3596. //
  3597. ReadList->List[ReadList->NumberOfEntries].Alignment = 0;
  3598. ReadList->NumberOfEntries++;
  3599. NumPagesToPrefetch++;
  3600. //
  3601. // Note that if we are prefetching only part of the
  3602. // scenario, we do not check to see if we've
  3603. // prefetched enough pages here. This is to avoid
  3604. // having to prefetch the header page twice in case it
  3605. // maxes the number of pages to prefetch and
  3606. // PrefetchSections is called again.
  3607. //
  3608. AddedHeaderPage = TRUE;
  3609. }
  3610. }
  3611. //
  3612. // Go through all the pages in the section and put offsets for
  3613. // pages to prefetch into the readlist.
  3614. //
  3615. PageIdx = SectionRecord->FirstPageIdx;
  3616. NumSectionPages = 0;
  3617. PreviousPageIdx = PF_INVALID_PAGE_IDX;
  3618. while (PageIdx != PF_INVALID_PAGE_IDX) {
  3619. PageRecord = &PageRecords[PageIdx];
  3620. //
  3621. // Update the number of pages we've seen on the list so
  3622. // far. If it is greater than what there should be on the
  3623. // list we have a problem. We may have even hit a loop. We
  3624. // should have caught this when we verified the scenario.
  3625. //
  3626. NumSectionPages++;
  3627. if (NumSectionPages > SectionRecord->NumPages) {
  3628. DBGPR((CCPFID,PFWARN,"CCPF: PrefetchSections-Corrupt0\n"));
  3629. Status = STATUS_INVALID_PARAMETER;
  3630. CCPF_ASSERT(FALSE);
  3631. goto cleanup;
  3632. }
  3633. //
  3634. // Get the index for the next page in the list.
  3635. //
  3636. PageIdx = PageRecord->NextPageIdx;
  3637. //
  3638. // If we are prefetching parts of the scenario and this is
  3639. // the first section, skip the pages up to the start
  3640. // cursor. Note that NumSectionPages has already been
  3641. // incremented above.
  3642. //
  3643. if (PrefetchingPartOfScenario &&
  3644. StartSectionNumber == SectionIdx &&
  3645. NumSectionPages <= StartPageNumber) {
  3646. continue;
  3647. }
  3648. //
  3649. // Skip pages we have marked "ignore" for some reason.
  3650. //
  3651. if (PageRecord->IsIgnore) {
  3652. continue;
  3653. }
  3654. //
  3655. // Except for the header page, we should not have put
  3656. // more entries into the read list then the number of
  3657. // pages for the section in the scenario file.
  3658. //
  3659. if (ReadList->NumberOfEntries > SectionRecord->NumPages + 1) {
  3660. DBGPR((CCPFID,PFWARN,"CCPF: PrefetchSections-Corrupt1\n"));
  3661. Status = STATUS_INVALID_PARAMETER;
  3662. CCPF_ASSERT(FALSE);
  3663. goto cleanup;
  3664. }
  3665. //
  3666. // Add this page to the list only if it's type (image
  3667. // or data) matches the type of pages we are prefetching.
  3668. //
  3669. if (((PrefetchingImagePages == FALSE) && !PageRecord->IsData) ||
  3670. ((PrefetchingImagePages == TRUE) && !PageRecord->IsImage)) {
  3671. continue;
  3672. }
  3673. //
  3674. // If we already added the header page to the list,
  3675. // don't add another entry for the same offset.
  3676. //
  3677. if (AddedHeaderPage && (PageRecord->FileOffset == 0)) {
  3678. continue;
  3679. }
  3680. //
  3681. // Check to see if this page comes after the last page
  3682. // we put in the read list. Perform this check as the
  3683. // very last check before adding the page to the
  3684. // readlist.
  3685. //
  3686. if (ReadList->NumberOfEntries) {
  3687. LastOffset = ReadList->List[ReadList->NumberOfEntries - 1].Alignment;
  3688. if (PageRecord->FileOffset <= (ULONG) LastOffset) {
  3689. DBGPR((CCPFID,PFWARN,"CCPF: PrefetchSections-Corrupt2\n"));
  3690. Status = STATUS_INVALID_PARAMETER;
  3691. CCPF_ASSERT(FALSE);
  3692. goto cleanup;
  3693. }
  3694. }
  3695. //
  3696. // Add this page to the readlist for this section.
  3697. //
  3698. ReadList->List[ReadList->NumberOfEntries].Alignment = PageRecord->FileOffset;
  3699. ReadList->NumberOfEntries++;
  3700. //
  3701. // Update number of pages we are asking mm to bring for us.
  3702. //
  3703. NumPagesToPrefetch++;
  3704. //
  3705. // Break out if we are prefetching requested number of
  3706. // pages.
  3707. //
  3708. if (PrefetchingPartOfScenario &&
  3709. NumPagesToPrefetch >= TotalPagesToPrefetch) {
  3710. break;
  3711. }
  3712. }
  3713. if (ReadList->NumberOfEntries) {
  3714. //
  3715. // Get the section object.
  3716. //
  3717. RtlInitUnicodeString(&SectionName, FilePath);
  3718. Status = CcPfGetSectionObject(&SectionName,
  3719. PrefetchingImagePages,
  3720. &SectionObject,
  3721. &FileObject,
  3722. &FileHandle);
  3723. if (!NT_SUCCESS(Status)) {
  3724. if (Status == STATUS_SHARING_VIOLATION) {
  3725. //
  3726. // We cannot open registry files due to sharing
  3727. // violation. Pass the file name and readlist to
  3728. // registry in case this is a registry file.
  3729. //
  3730. CmPrefetchHivePages(&SectionName, ReadList);
  3731. }
  3732. //
  3733. // Free the built read list.
  3734. //
  3735. ExFreePool(ReadList);
  3736. ReadList = NULL;
  3737. continue;
  3738. }
  3739. //
  3740. // We should have got a file object and a section object
  3741. // pointer if we created the section successfully.
  3742. //
  3743. CCPF_ASSERT(FileObject != NULL && SectionObject != NULL);
  3744. ReadList->FileObject = FileObject;
  3745. //
  3746. // Put data into the tables, so we know what to cleanup.
  3747. //
  3748. ReadLists[NumReadLists] = ReadList;
  3749. FileHandleTable[NumReadLists] = FileHandle;
  3750. FileObjectTable[NumReadLists] = FileObject;
  3751. SectionObjectTable[NumReadLists] = SectionObject;
  3752. NumReadLists++;
  3753. } else {
  3754. //
  3755. // We won't be prefetching anything for this section.
  3756. //
  3757. ExFreePool(ReadList);
  3758. }
  3759. //
  3760. // Reset these so we know what to cleanup.
  3761. //
  3762. ReadList = NULL;
  3763. FileHandle = NULL;
  3764. FileObject = NULL;
  3765. SectionObject = NULL;
  3766. //
  3767. // Break out if we are prefetching requested number of
  3768. // pages.
  3769. //
  3770. if (PrefetchingPartOfScenario &&
  3771. NumPagesToPrefetch == TotalPagesToPrefetch) {
  3772. break;
  3773. }
  3774. }
  3775. //
  3776. // If prefetching only part of the the scenario, update return
  3777. // values.
  3778. //
  3779. if (PrefetchingPartOfScenario) {
  3780. if (NumPagesPrefetched) {
  3781. *NumPagesPrefetched = NumPagesToPrefetch;
  3782. }
  3783. if (EndCursor) {
  3784. //
  3785. // If we did the last page of the current section, then
  3786. // start from the next section. Otherwise start from the
  3787. // next page in this section.
  3788. //
  3789. if (PageIdx == PF_INVALID_PAGE_IDX) {
  3790. EndCursor->SectionIdx = SectionIdx + 1;
  3791. EndCursor->PageIdx = 0;
  3792. } else {
  3793. EndCursor->SectionIdx = SectionIdx;
  3794. EndCursor->PageIdx = NumSectionPages;
  3795. }
  3796. //
  3797. // Make sure the end position is equal to or greater than
  3798. // start position.
  3799. //
  3800. if (EndCursor->SectionIdx < StartSectionNumber) {
  3801. EndCursor->SectionIdx = StartSectionNumber;
  3802. }
  3803. if (EndCursor->SectionIdx == StartSectionNumber) {
  3804. if (EndCursor->PageIdx < StartPageNumber) {
  3805. EndCursor->PageIdx = StartPageNumber;
  3806. }
  3807. }
  3808. }
  3809. }
  3810. //
  3811. // Ask Mm to process the readlists only if we actually have pages
  3812. // to ask for.
  3813. //
  3814. if (NumReadLists) {
  3815. if (NumPagesToPrefetch) {
  3816. DBGPR((CCPFID,PFPRFD,"CCPF: Prefetching %d sections %d pages\n",
  3817. NumReadLists, NumPagesToPrefetch));
  3818. Status = MmPrefetchPages(NumReadLists, ReadLists);
  3819. } else {
  3820. Status = STATUS_UNSUCCESSFUL;
  3821. //
  3822. // We cannot have any read lists if we don't have any
  3823. // pages to prefetch.
  3824. //
  3825. }
  3826. } else {
  3827. Status = STATUS_SUCCESS;
  3828. }
  3829. cleanup:
  3830. if (Tables) {
  3831. for (ReadListIdx = 0; ReadListIdx < NumReadLists; ReadListIdx++) {
  3832. if (ReadLists[ReadListIdx]) {
  3833. ExFreePool(ReadLists[ReadListIdx]);
  3834. }
  3835. if (FileHandleTable[ReadListIdx]) {
  3836. ZwClose(FileHandleTable[ReadListIdx]);
  3837. }
  3838. if (FileObjectTable[ReadListIdx]) {
  3839. ObDereferenceObject(FileObjectTable[ReadListIdx]);
  3840. }
  3841. if (SectionObjectTable[ReadListIdx]) {
  3842. ObDereferenceObject(SectionObjectTable[ReadListIdx]);
  3843. }
  3844. }
  3845. ExFreePool(Tables);
  3846. }
  3847. if (ReadList) {
  3848. ExFreePool(ReadList);
  3849. }
  3850. if (FileHandle) {
  3851. ZwClose(FileHandle);
  3852. }
  3853. if (FileObject) {
  3854. ObDereferenceObject(FileObject);
  3855. }
  3856. if (SectionObject) {
  3857. ObDereferenceObject(SectionObject);
  3858. }
  3859. DBGPR((CCPFID,PFPREF,"CCPF: PrefetchSections(%p)=%x,%d,%d\n",
  3860. PrefetchHeader, Status, NumReadLists, NumPagesToPrefetch));
  3861. return Status;
  3862. }
  3863. NTSTATUS
  3864. CcPfPrefetchMetadata(
  3865. IN PCCPF_PREFETCH_HEADER PrefetchHeader
  3866. )
  3867. /*++
  3868. Routine Description:
  3869. This routine tries to prefetch the filesystem metadata that will
  3870. be needed to prefetch pages for the scenario, so metadata I/Os do
  3871. not get in the way of efficient page prefetch I/O.
  3872. This function should be called only after the prefetch header has
  3873. been initialized and the routine to open the volumes for prefetch
  3874. has been called.
  3875. Arguments:
  3876. PrefetchHeader - Pointer to prefetch header.
  3877. Return Value:
  3878. Status.
  3879. Environment:
  3880. Kernel mode, IRQL == PASSIVE_LEVEL.
  3881. --*/
  3882. {
  3883. PCHAR MetadataInfoBase;
  3884. PPF_METADATA_RECORD MetadataRecordTable;
  3885. PPF_METADATA_RECORD MetadataRecord;
  3886. PWCHAR VolumePath;
  3887. PFILE_PREFETCH FilePrefetchInfo;
  3888. PPF_SCENARIO_HEADER Scenario;
  3889. PPF_COUNTED_STRING DirectoryPath;
  3890. PCCPF_PREFETCH_VOLUME_INFO VolumeNode;
  3891. ULONG MetadataRecordIdx;
  3892. ULONG DirectoryIdx;
  3893. NTSTATUS Status;
  3894. //
  3895. // Initialize locals.
  3896. //
  3897. Scenario = PrefetchHeader->Scenario;
  3898. if (Scenario == NULL) {
  3899. CCPF_ASSERT(Scenario);
  3900. Status = STATUS_INVALID_PARAMETER;
  3901. goto cleanup;
  3902. }
  3903. DBGPR((CCPFID,PFPREF,"CCPF: PrefetchMetadata(%p)\n",PrefetchHeader));
  3904. //
  3905. // Get pointer to metadata prefetch information.
  3906. //
  3907. MetadataInfoBase = (PCHAR)Scenario + Scenario->MetadataInfoOffset;
  3908. MetadataRecordTable = (PPF_METADATA_RECORD) MetadataInfoBase;
  3909. //
  3910. // Go through and prefetch requested metadata from volumes.
  3911. //
  3912. for (MetadataRecordIdx = 0;
  3913. MetadataRecordIdx < Scenario->NumMetadataRecords;
  3914. MetadataRecordIdx++) {
  3915. MetadataRecord = &MetadataRecordTable[MetadataRecordIdx];
  3916. VolumePath = (PWCHAR)
  3917. (MetadataInfoBase + MetadataRecord->VolumeNameOffset);
  3918. //
  3919. // Find the volume node for this volume containing opened handle.
  3920. //
  3921. VolumeNode = CcPfFindPrefetchVolumeInfoInList(VolumePath,
  3922. &PrefetchHeader->OpenedVolumeList);
  3923. if (!VolumeNode) {
  3924. //
  3925. // If it is not in the opened volume list, it should be in the
  3926. // bad volume list (because it was not mounted, or its serial
  3927. // did not match etc.)
  3928. //
  3929. CCPF_ASSERT(CcPfFindPrefetchVolumeInfoInList(VolumePath, &PrefetchHeader->BadVolumeList));
  3930. //
  3931. // We cannot prefetch metadata on this volume.
  3932. //
  3933. continue;
  3934. } else {
  3935. //
  3936. // We should have already opened a handle to this volume.
  3937. //
  3938. CCPF_ASSERT(VolumeNode->VolumeHandle);
  3939. }
  3940. //
  3941. // Prefetch MFT entries and such for the files and directories
  3942. // we will access.
  3943. //
  3944. FilePrefetchInfo = (PFILE_PREFETCH)
  3945. (MetadataInfoBase + MetadataRecord->FilePrefetchInfoOffset);
  3946. Status = CcPfPrefetchFileMetadata(VolumeNode->VolumeHandle, FilePrefetchInfo);
  3947. //
  3948. // Walk through the contents of the directories sequentially
  3949. // so we don't jump around when opening the files. The
  3950. // directory list is sorted, so we will prefetch the parent
  3951. // directories before children.
  3952. //
  3953. DirectoryPath = (PPF_COUNTED_STRING)
  3954. (MetadataInfoBase + MetadataRecord->DirectoryPathsOffset);
  3955. for (DirectoryIdx = 0;
  3956. DirectoryIdx < MetadataRecord->NumDirectories;
  3957. DirectoryIdx++) {
  3958. Status = CcPfPrefetchDirectoryContents(DirectoryPath->String,
  3959. DirectoryPath->Length);
  3960. if (Status == STATUS_UNRECOGNIZED_VOLUME ||
  3961. Status == STATUS_INVALID_PARAMETER) {
  3962. //
  3963. // This volume may not have been mounted or got dismounted.
  3964. //
  3965. break;
  3966. }
  3967. //
  3968. // Get next directory.
  3969. //
  3970. DirectoryPath = (PPF_COUNTED_STRING)
  3971. (&DirectoryPath->String[DirectoryPath->Length + 1]);
  3972. }
  3973. }
  3974. Status = STATUS_SUCCESS;
  3975. cleanup:
  3976. DBGPR((CCPFID,PFPREF,"CCPF: PrefetchMetadata(%p)=%x\n",PrefetchHeader,Status));
  3977. return Status;
  3978. }
  3979. NTSTATUS
  3980. CcPfPrefetchFileMetadata(
  3981. HANDLE VolumeHandle,
  3982. PFILE_PREFETCH FilePrefetch
  3983. )
  3984. /*++
  3985. Routine Description:
  3986. This routine issues the specified metadata prefetch request to the
  3987. file system.
  3988. Arguments:
  3989. VolumeHandle - Volume this request should be issued to.
  3990. FilePrefetch - POinter to prefetch request.
  3991. Return Value:
  3992. Status.
  3993. Environment:
  3994. Kernel mode, IRQL == PASSIVE_LEVEL.
  3995. --*/
  3996. {
  3997. PFILE_PREFETCH SplitFilePrefetch;
  3998. IO_STATUS_BLOCK IoStatusBlock;
  3999. ULONG FilePrefetchSize;
  4000. ULONG CurrentFileMetadataIdx;
  4001. ULONG NumFileMetadataToPrefetch;
  4002. ULONG RemainingFileMetadata;
  4003. ULONG CopySize;
  4004. NTSTATUS Status;
  4005. //
  4006. // Initialize locals.
  4007. //
  4008. SplitFilePrefetch = NULL;
  4009. Status = STATUS_SUCCESS;
  4010. DBGPR((CCPFID,PFPRFD,"CCPF: PrefetchFileMetadata(%p)\n", FilePrefetch));
  4011. //
  4012. // If the number of file prefetch entries are small, simply pass the
  4013. // buffer in the scenario instructions to the file system.
  4014. //
  4015. if (FilePrefetch->Count < CCPF_MAX_FILE_METADATA_PREFETCH_COUNT) {
  4016. FilePrefetchSize = sizeof(FILE_PREFETCH);
  4017. if (FilePrefetch->Count) {
  4018. FilePrefetchSize += (FilePrefetch->Count - 1) * sizeof(ULONGLONG);
  4019. }
  4020. Status = ZwFsControlFile(VolumeHandle,
  4021. NULL,
  4022. NULL,
  4023. NULL,
  4024. &IoStatusBlock,
  4025. FSCTL_FILE_PREFETCH,
  4026. FilePrefetch,
  4027. FilePrefetchSize,
  4028. NULL,
  4029. 0);
  4030. } else {
  4031. //
  4032. // We need to allocate an intermediary buffer and split up the
  4033. // requests.
  4034. //
  4035. FilePrefetchSize = sizeof(FILE_PREFETCH);
  4036. FilePrefetchSize += (CCPF_MAX_FILE_METADATA_PREFETCH_COUNT - 1) * sizeof(ULONGLONG);
  4037. SplitFilePrefetch = ExAllocatePoolWithTag(PagedPool,
  4038. FilePrefetchSize,
  4039. CCPF_ALLOC_METADATA_TAG);
  4040. if (!SplitFilePrefetch) {
  4041. Status = STATUS_INSUFFICIENT_RESOURCES;
  4042. goto cleanup;
  4043. }
  4044. //
  4045. // Copy header.
  4046. //
  4047. *SplitFilePrefetch = *FilePrefetch;
  4048. for (CurrentFileMetadataIdx = 0;
  4049. CurrentFileMetadataIdx < FilePrefetch->Count;
  4050. CurrentFileMetadataIdx += NumFileMetadataToPrefetch) {
  4051. //
  4052. // Calculate how many more file metadata entries we have to prefetch.
  4053. // Adjust it so we don't go beyond FilePrefetch->Count.
  4054. //
  4055. NumFileMetadataToPrefetch = CCPF_MAX_FILE_METADATA_PREFETCH_COUNT;
  4056. RemainingFileMetadata = FilePrefetch->Count - CurrentFileMetadataIdx;
  4057. if (NumFileMetadataToPrefetch > RemainingFileMetadata) {
  4058. NumFileMetadataToPrefetch = RemainingFileMetadata;
  4059. }
  4060. //
  4061. // Update the count on header.
  4062. //
  4063. SplitFilePrefetch->Count = NumFileMetadataToPrefetch;
  4064. //
  4065. // Copy over the file metadata indices.
  4066. //
  4067. CopySize = NumFileMetadataToPrefetch * sizeof(ULONGLONG);
  4068. RtlCopyMemory(SplitFilePrefetch->Prefetch,
  4069. &FilePrefetch->Prefetch[CurrentFileMetadataIdx],
  4070. CopySize);
  4071. //
  4072. // Calculate the request size.
  4073. //
  4074. CCPF_ASSERT(SplitFilePrefetch->Count);
  4075. CCPF_ASSERT(SplitFilePrefetch->Count <= CCPF_MAX_FILE_METADATA_PREFETCH_COUNT);
  4076. FilePrefetchSize = sizeof(FILE_PREFETCH);
  4077. FilePrefetchSize += (SplitFilePrefetch->Count - 1) * sizeof(ULONGLONG);
  4078. //
  4079. // Issue the request.
  4080. //
  4081. Status = ZwFsControlFile(VolumeHandle,
  4082. NULL,
  4083. NULL,
  4084. NULL,
  4085. &IoStatusBlock,
  4086. FSCTL_FILE_PREFETCH,
  4087. SplitFilePrefetch,
  4088. FilePrefetchSize,
  4089. NULL,
  4090. 0);
  4091. if (NT_ERROR(Status)) {
  4092. goto cleanup;
  4093. }
  4094. }
  4095. }
  4096. //
  4097. // Fall through with status.
  4098. //
  4099. cleanup:
  4100. if (SplitFilePrefetch) {
  4101. ExFreePool(SplitFilePrefetch);
  4102. }
  4103. DBGPR((CCPFID,PFPRFD,"CCPF: PrefetchFileMetadata()=%x\n", Status));
  4104. return Status;
  4105. }
  4106. NTSTATUS
  4107. CcPfPrefetchDirectoryContents(
  4108. WCHAR *DirectoryPath,
  4109. WCHAR DirectoryPathlength
  4110. )
  4111. /*++
  4112. Routine Description:
  4113. This routine attempts to prefetch the contents of a directory.
  4114. Arguments:
  4115. DirectoryPath - NUL terminated path.
  4116. DirectoryPathLength - Number of characters exclusing terminating NUL.
  4117. Return Value:
  4118. Status.
  4119. Environment:
  4120. Kernel mode, IRQL == PASSIVE_LEVEL.
  4121. --*/
  4122. {
  4123. NTSTATUS Status;
  4124. HANDLE DirectoryHandle;
  4125. UNICODE_STRING DirectoryPathU;
  4126. OBJECT_ATTRIBUTES ObjectAttributes;
  4127. IO_STATUS_BLOCK IoStatusBlock;
  4128. BOOLEAN OpenedDirectory;
  4129. PVOID QueryBuffer;
  4130. ULONG QueryBufferSize;
  4131. ULONG QueryIdx;
  4132. BOOLEAN RestartScan;
  4133. UNREFERENCED_PARAMETER (DirectoryPathlength);
  4134. //
  4135. // Initialize locals.
  4136. //
  4137. OpenedDirectory = FALSE;
  4138. QueryBuffer = NULL;
  4139. DBGPR((CCPFID,PFPRFD,"CCPF: PrefetchDirectory(%ws)\n",DirectoryPath));
  4140. //
  4141. // Open the directory.
  4142. //
  4143. RtlInitUnicodeString(&DirectoryPathU, DirectoryPath);
  4144. InitializeObjectAttributes(&ObjectAttributes,
  4145. &DirectoryPathU,
  4146. OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
  4147. NULL,
  4148. NULL);
  4149. Status = ZwCreateFile(&DirectoryHandle,
  4150. FILE_LIST_DIRECTORY | SYNCHRONIZE,
  4151. &ObjectAttributes,
  4152. &IoStatusBlock,
  4153. 0,
  4154. 0,
  4155. FILE_SHARE_READ |
  4156. FILE_SHARE_WRITE |
  4157. FILE_SHARE_DELETE,
  4158. FILE_OPEN,
  4159. FILE_DIRECTORY_FILE |
  4160. FILE_SYNCHRONOUS_IO_NONALERT |
  4161. FILE_OPEN_FOR_BACKUP_INTENT,
  4162. NULL,
  4163. 0);
  4164. if (!NT_SUCCESS(Status)) {
  4165. goto cleanup;
  4166. }
  4167. OpenedDirectory = TRUE;
  4168. //
  4169. // Allocate a big query buffer so we have to make only a small
  4170. // number of calls to cause the file system to walk through the
  4171. // contents of the directory.
  4172. //
  4173. QueryBufferSize = 4 * PAGE_SIZE;
  4174. QueryBuffer = ExAllocatePoolWithTag(PagedPool | POOL_COLD_ALLOCATION,
  4175. QueryBufferSize,
  4176. CCPF_ALLOC_QUERY_TAG);
  4177. if (!QueryBuffer) {
  4178. Status = STATUS_INSUFFICIENT_RESOURCES;
  4179. goto cleanup;
  4180. }
  4181. //
  4182. // Query names of files in the directory hopefully causing the
  4183. // file system to touch the directory contents sequentially. If
  4184. // the directory is really big, we don't want to attempt to bring
  4185. // it all in, so we limit the number of times we query.
  4186. //
  4187. // Assuming filenames are 16 characters long on average, we can
  4188. // fit 32 filenames in 1KB, 128 on an x86 page. A 4 page query
  4189. // buffer holds 512 file names. If we do it 10 times, we end up
  4190. // prefetching data for about 5000 files.
  4191. //
  4192. RestartScan = TRUE;
  4193. for (QueryIdx = 0; QueryIdx < 10; QueryIdx++) {
  4194. Status = ZwQueryDirectoryFile(DirectoryHandle,
  4195. NULL,
  4196. NULL,
  4197. NULL,
  4198. &IoStatusBlock,
  4199. QueryBuffer,
  4200. QueryBufferSize,
  4201. FileNamesInformation,
  4202. FALSE,
  4203. NULL,
  4204. RestartScan);
  4205. RestartScan = FALSE;
  4206. if (!NT_SUCCESS(Status)) {
  4207. //
  4208. // If the status is that we got all the files, we are done.
  4209. //
  4210. if (Status == STATUS_NO_MORE_FILES) {
  4211. break;
  4212. }
  4213. goto cleanup;
  4214. }
  4215. }
  4216. Status = STATUS_SUCCESS;
  4217. cleanup:
  4218. if (QueryBuffer) {
  4219. ExFreePool(QueryBuffer);
  4220. }
  4221. if (OpenedDirectory) {
  4222. ZwClose(DirectoryHandle);
  4223. }
  4224. DBGPR((CCPFID,PFPRFD,"CCPF: PrefetchDirectory(%ws)=%x\n",DirectoryPath, Status));
  4225. return Status;
  4226. }
  4227. VOID
  4228. CcPfInitializePrefetchHeader (
  4229. OUT PCCPF_PREFETCH_HEADER PrefetchHeader
  4230. )
  4231. /*++
  4232. Routine Description:
  4233. This routine initalizes the prefetch header fields.
  4234. Arguments:
  4235. PrefetchHeader - Pointer to prefetch header.
  4236. Return Value:
  4237. None.
  4238. Environment:
  4239. Kernel mode, IRQL == PASSIVE_LEVEL.
  4240. --*/
  4241. {
  4242. //
  4243. // Zero out the structure. This initializes the following:
  4244. //
  4245. // Scenario
  4246. // VolumeNodes
  4247. //
  4248. RtlZeroMemory(PrefetchHeader, sizeof(CCPF_PREFETCH_HEADER));
  4249. //
  4250. // Initialize the volume lists.
  4251. //
  4252. InitializeListHead(&PrefetchHeader->BadVolumeList);
  4253. InitializeListHead(&PrefetchHeader->OpenedVolumeList);
  4254. }
  4255. VOID
  4256. CcPfCleanupPrefetchHeader (
  4257. IN PCCPF_PREFETCH_HEADER PrefetchHeader
  4258. )
  4259. /*++
  4260. Routine Description:
  4261. This routine cleans up allocations / references in the
  4262. PrefetchHeader. It does not free the structure itself.
  4263. Arguments:
  4264. PrefetchHeader - Prefetch header to cleanup.
  4265. Return Value:
  4266. None.
  4267. Environment:
  4268. Kernel mode, IRQL == PASSIVE_LEVEL.
  4269. --*/
  4270. {
  4271. PCCPF_PREFETCH_VOLUME_INFO VolumeNode;
  4272. PLIST_ENTRY RemovedEntry;
  4273. DBGPR((CCPFID,PFTRC,"CCPF: CleanupPrefetchHeader(%p)\n", PrefetchHeader));
  4274. //
  4275. // Walk the opened volumes list and close the handles.
  4276. //
  4277. while (!IsListEmpty(&PrefetchHeader->OpenedVolumeList)) {
  4278. RemovedEntry = RemoveHeadList(&PrefetchHeader->OpenedVolumeList);
  4279. VolumeNode = CONTAINING_RECORD(RemovedEntry,
  4280. CCPF_PREFETCH_VOLUME_INFO,
  4281. VolumeLink);
  4282. CCPF_ASSERT(VolumeNode->VolumeHandle);
  4283. ZwClose(VolumeNode->VolumeHandle);
  4284. }
  4285. //
  4286. // Free allocated volume nodes.
  4287. //
  4288. if (PrefetchHeader->VolumeNodes) {
  4289. ExFreePool(PrefetchHeader->VolumeNodes);
  4290. }
  4291. }
  4292. NTSTATUS
  4293. CcPfGetPrefetchInstructions(
  4294. IN PPF_SCENARIO_ID ScenarioId,
  4295. IN PF_SCENARIO_TYPE ScenarioType,
  4296. OUT PPF_SCENARIO_HEADER *ScenarioHeader
  4297. )
  4298. /*++
  4299. Routine Description:
  4300. This routine checks for prefetch instructions for the specified
  4301. scenario, verifies them and returns them in an allocated buffer
  4302. from paged pool the caller should free.
  4303. Arguments:
  4304. ScenarioId - Scenario identifier.
  4305. ScenarioType - Scenario type.
  4306. Scenario - Where pointer to allocated buffer should be put.
  4307. Return Value:
  4308. Status.
  4309. Environment:
  4310. Kernel mode, IRQL == PASSIVE_LEVEL.
  4311. --*/
  4312. {
  4313. NTSTATUS Status;
  4314. PWSTR SystemRootPath = L"\\SystemRoot";
  4315. PWSTR FilePath;
  4316. UNICODE_STRING ScenarioFilePath;
  4317. ULONG FilePathSize;
  4318. HANDLE ScenarioFile;
  4319. PPF_SCENARIO_HEADER Scenario;
  4320. ULONG ScenarioSize;
  4321. OBJECT_ATTRIBUTES ObjectAttributes;
  4322. IO_STATUS_BLOCK IoStatus;
  4323. FILE_STANDARD_INFORMATION StandardInfo;
  4324. ULONG FailedCheck;
  4325. BOOLEAN OpenedScenarioFile;
  4326. PKTHREAD CurrentThread;
  4327. //
  4328. // Initialize locals.
  4329. //
  4330. FilePath = NULL;
  4331. Scenario = NULL;
  4332. OpenedScenarioFile = FALSE;
  4333. DBGPR((CCPFID,PFPREF,"CCPF: GetInstructions(%ws)\n", ScenarioId->ScenName));
  4334. //
  4335. // Hold the parameters lock while building path to instructions so
  4336. // RootDirPath does not change beneath our feet.
  4337. //
  4338. CurrentThread = KeGetCurrentThread ();
  4339. KeEnterCriticalRegionThread(CurrentThread);
  4340. ExAcquireResourceSharedLite(&CcPfGlobals.Parameters.ParametersLock, TRUE);
  4341. //
  4342. // Build file path for prefetch instructions for this scenario
  4343. // id. +1 to wcslen(SystemRootPath) is for the "\" after it. The last
  4344. // sizeof(WCHAR) is added for the terminating NUL.
  4345. //
  4346. FilePathSize = (wcslen(SystemRootPath) + 1) * sizeof(WCHAR);
  4347. FilePathSize += wcslen(CcPfGlobals.Parameters.Parameters.RootDirPath) * sizeof(WCHAR);
  4348. FilePathSize += PF_MAX_SCENARIO_FILE_NAME * sizeof(WCHAR);
  4349. FilePathSize += sizeof(WCHAR);
  4350. FilePath = ExAllocatePoolWithTag(PagedPool | POOL_COLD_ALLOCATION,
  4351. FilePathSize,
  4352. CCPF_ALLOC_FILENAME_TAG);
  4353. if (!FilePath) {
  4354. ExReleaseResourceLite(&CcPfGlobals.Parameters.ParametersLock);
  4355. KeLeaveCriticalRegionThread(CurrentThread);
  4356. Status = STATUS_INSUFFICIENT_RESOURCES;
  4357. goto cleanup;
  4358. }
  4359. swprintf(FilePath,
  4360. L"%s\\%s\\" PF_SCEN_FILE_NAME_FORMAT,
  4361. SystemRootPath,
  4362. CcPfGlobals.Parameters.Parameters.RootDirPath,
  4363. ScenarioId->ScenName,
  4364. ScenarioId->HashId,
  4365. PF_PREFETCH_FILE_EXTENSION);
  4366. //
  4367. // Release the parameters lock.
  4368. //
  4369. ExReleaseResourceLite(&CcPfGlobals.Parameters.ParametersLock);
  4370. KeLeaveCriticalRegionThread(CurrentThread);
  4371. //
  4372. // Open the scenario file. We open the file exlusive so we do not
  4373. // end up with half a file when the service is updating it etc.
  4374. //
  4375. DBGPR((CCPFID,PFPRFD,"CCPF: GetInstructions-[%ws]\n", FilePath));
  4376. RtlInitUnicodeString(&ScenarioFilePath, FilePath);
  4377. InitializeObjectAttributes(&ObjectAttributes,
  4378. &ScenarioFilePath,
  4379. OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
  4380. NULL,
  4381. NULL);
  4382. Status = ZwOpenFile(&ScenarioFile,
  4383. GENERIC_READ | SYNCHRONIZE,
  4384. &ObjectAttributes,
  4385. &IoStatus,
  4386. 0,
  4387. FILE_SYNCHRONOUS_IO_NONALERT);
  4388. if (!NT_SUCCESS(Status)) {
  4389. DBGPR((CCPFID,PFWARN,"CCPF: GetInstructions-FailedOpenFile\n"));
  4390. goto cleanup;
  4391. }
  4392. OpenedScenarioFile = TRUE;
  4393. //
  4394. // Get file size. If it is too big or too small, give up.
  4395. //
  4396. Status = ZwQueryInformationFile(ScenarioFile,
  4397. &IoStatus,
  4398. &StandardInfo,
  4399. sizeof(StandardInfo),
  4400. FileStandardInformation);
  4401. if (!NT_SUCCESS(Status)) {
  4402. DBGPR((CCPFID,PFWARN,"CCPF: GetInstructions-FailedGetInfo\n"));
  4403. goto cleanup;
  4404. }
  4405. ScenarioSize = StandardInfo.EndOfFile.LowPart;
  4406. if (ScenarioSize > PF_MAXIMUM_SCENARIO_SIZE ||
  4407. ScenarioSize == 0 ||
  4408. StandardInfo.EndOfFile.HighPart) {
  4409. DBGPR((CCPFID,PFWARN,"CCPF: GetInstructions-FileTooBig\n"));
  4410. Status = STATUS_UNSUCCESSFUL;
  4411. goto cleanup;
  4412. }
  4413. //
  4414. // Allocate scenario buffer.
  4415. //
  4416. Scenario = ExAllocatePoolWithTag(PagedPool,
  4417. ScenarioSize,
  4418. CCPF_ALLOC_PREFSCEN_TAG);
  4419. if (!Scenario) {
  4420. Status = STATUS_INSUFFICIENT_RESOURCES;
  4421. goto cleanup;
  4422. }
  4423. //
  4424. // Read the scenario file.
  4425. //
  4426. Status = ZwReadFile(ScenarioFile,
  4427. 0,
  4428. 0,
  4429. 0,
  4430. &IoStatus,
  4431. Scenario,
  4432. ScenarioSize,
  4433. 0,
  4434. 0);
  4435. if (!NT_SUCCESS(Status)) {
  4436. DBGPR((CCPFID,PFWARN,"CCPF: GetInstructions-FailedRead\n"));
  4437. goto cleanup;
  4438. }
  4439. //
  4440. // Verify the scenario file.
  4441. //
  4442. if (!PfVerifyScenarioBuffer(Scenario, ScenarioSize, &FailedCheck)) {
  4443. DBGPR((CCPFID,PFWARN,"CCPF: GetInstructions-FailedVerify\n"));
  4444. Status = STATUS_UNSUCCESSFUL;
  4445. goto cleanup;
  4446. }
  4447. //
  4448. // Verify that the scenario type matches.
  4449. //
  4450. if (Scenario->ScenarioType != ScenarioType) {
  4451. DBGPR((CCPFID,PFWARN,"CCPF: GetInstructions-ScenTypeMismatch\n"));
  4452. Status = STATUS_UNSUCCESSFUL;
  4453. goto cleanup;
  4454. }
  4455. //
  4456. // Setup return pointer.
  4457. //
  4458. *ScenarioHeader = Scenario;
  4459. Status = STATUS_SUCCESS;
  4460. cleanup:
  4461. if (OpenedScenarioFile) {
  4462. ZwClose(ScenarioFile);
  4463. }
  4464. if (FilePath) {
  4465. ExFreePool(FilePath);
  4466. }
  4467. if (!NT_SUCCESS(Status)) {
  4468. if (Scenario) {
  4469. ExFreePool(Scenario);
  4470. }
  4471. }
  4472. DBGPR((CCPFID,PFPREF,"CCPF: GetInstructions(%ws)=%x,%p\n", ScenarioId->ScenName, Status, Scenario));
  4473. return Status;
  4474. }
  4475. NTSTATUS
  4476. CcPfQueryScenarioInformation(
  4477. IN PPF_SCENARIO_HEADER Scenario,
  4478. IN CCPF_SCENARIO_INFORMATION_TYPE InformationType,
  4479. OUT PVOID Buffer,
  4480. IN ULONG BufferSize,
  4481. OUT PULONG RequiredSize
  4482. )
  4483. /*++
  4484. Routine Description:
  4485. This routine gathers requested information from the scenario structure.
  4486. Arguments:
  4487. Scenario - Pointer to scenario.
  4488. InformationType - Type of information requested.
  4489. Buffer - Where requested information will be put.
  4490. BufferSize - Max size of buffer in bytes.
  4491. RequiredSize - How big the buffer should be if it is too small.
  4492. Return Value:
  4493. Status.
  4494. Environment:
  4495. Kernel mode, IRQL == PASSIVE_LEVEL.
  4496. --*/
  4497. {
  4498. NTSTATUS Status;
  4499. PPF_SECTION_RECORD SectionRecord;
  4500. PPF_SECTION_RECORD SectionRecords;
  4501. ULONG SectionIdx;
  4502. PPF_PAGE_RECORD PageRecord;
  4503. PPF_PAGE_RECORD PageRecords;
  4504. PCHAR FileNameData;
  4505. LONG PageIdx;
  4506. PCCPF_BASIC_SCENARIO_INFORMATION BasicInfo;
  4507. PCCPF_BOOT_SCENARIO_INFORMATION BootInfo;
  4508. BOOLEAN AddedHeaderPage;
  4509. ULONG NumDataPages;
  4510. ULONG NumImagePages;
  4511. WCHAR *SectionName;
  4512. WCHAR *SectionNameSuffix;
  4513. WCHAR *SmssSuffix;
  4514. WCHAR *WinlogonSuffix;
  4515. WCHAR *SvchostSuffix;
  4516. WCHAR *UserinitSuffix;
  4517. ULONG SmssSuffixLength;
  4518. ULONG WinlogonSuffixLength;
  4519. ULONG SvchostSuffixLength;
  4520. ULONG UserinitSuffixLength;
  4521. CCPF_BOOT_SCENARIO_PHASE BootPhaseIdx;
  4522. //
  4523. // Initialize locals.
  4524. //
  4525. BootPhaseIdx = 0;
  4526. SmssSuffix = L"\\SYSTEM32\\SMSS.EXE";
  4527. SmssSuffixLength = wcslen(SmssSuffix);
  4528. WinlogonSuffix = L"\\SYSTEM32\\WINLOGON.EXE";
  4529. WinlogonSuffixLength = wcslen(WinlogonSuffix);
  4530. SvchostSuffix = L"\\SYSTEM32\\SVCHOST.EXE";
  4531. SvchostSuffixLength = wcslen(SvchostSuffix);
  4532. UserinitSuffix = L"\\SYSTEM32\\USERINIT.EXE";
  4533. UserinitSuffixLength = wcslen(UserinitSuffix);
  4534. DBGPR((CCPFID,PFTRC,"CCPF: QueryScenario(%p,%x,%p)\n",Scenario,InformationType,Buffer));
  4535. //
  4536. // Check requested information type.
  4537. //
  4538. if (InformationType >= CcPfMaxScenarioInformationType) {
  4539. Status = STATUS_INVALID_PARAMETER;
  4540. goto cleanup;
  4541. }
  4542. //
  4543. // Initialize pointers to data in the scenario.
  4544. //
  4545. SectionRecords = (PPF_SECTION_RECORD)
  4546. ((PCHAR) Scenario + Scenario->SectionInfoOffset);
  4547. PageRecords = (PPF_PAGE_RECORD)
  4548. ((PCHAR) Scenario + Scenario->PageInfoOffset);
  4549. FileNameData = (PCHAR) Scenario + Scenario->FileNameInfoOffset;
  4550. //
  4551. // Collect requested information.
  4552. //
  4553. switch(InformationType) {
  4554. case CcPfBasicScenarioInformation:
  4555. //
  4556. // Check buffer size.
  4557. //
  4558. if (BufferSize < sizeof(CCPF_BASIC_SCENARIO_INFORMATION)) {
  4559. *RequiredSize = sizeof(CCPF_BASIC_SCENARIO_INFORMATION);
  4560. Status = STATUS_BUFFER_TOO_SMALL;
  4561. goto cleanup;
  4562. }
  4563. //
  4564. // Initialize return buffer.
  4565. //
  4566. BasicInfo = Buffer;
  4567. RtlZeroMemory(BasicInfo, sizeof(CCPF_BASIC_SCENARIO_INFORMATION));
  4568. //
  4569. // Go through the scenario's sections.
  4570. //
  4571. for (SectionIdx = 0; SectionIdx < Scenario->NumSections; SectionIdx ++) {
  4572. SectionRecord = &SectionRecords[SectionIdx];
  4573. //
  4574. // Skip this section if it was marked ignore for some reason.
  4575. //
  4576. if (SectionRecord->IsIgnore) {
  4577. BasicInfo->NumIgnoredSections++;
  4578. continue;
  4579. }
  4580. //
  4581. // Initialize loop locals.
  4582. //
  4583. AddedHeaderPage = FALSE;
  4584. NumDataPages = 0;
  4585. NumImagePages = 0;
  4586. //
  4587. // Note that we will prefetch the header page as a data
  4588. // page if this section will be prefetched as image.
  4589. //
  4590. if (SectionRecord->IsImage) {
  4591. NumDataPages++;
  4592. AddedHeaderPage = TRUE;
  4593. }
  4594. //
  4595. // Go through the section's pages.
  4596. //
  4597. PageIdx = SectionRecord->FirstPageIdx;
  4598. while (PageIdx != PF_INVALID_PAGE_IDX) {
  4599. PageRecord = &PageRecords[PageIdx];
  4600. //
  4601. // Get the index for the next page in the list.
  4602. //
  4603. PageIdx = PageRecord->NextPageIdx;
  4604. //
  4605. // Skip pages we have marked "ignore" for some reason.
  4606. //
  4607. if (PageRecord->IsIgnore) {
  4608. BasicInfo->NumIgnoredPages++;
  4609. continue;
  4610. }
  4611. if (PageRecord->IsData) {
  4612. //
  4613. // If this page is the first page, count it only
  4614. // if we have not already counted the header page
  4615. // for image mapping.
  4616. //
  4617. if (PageRecord->FileOffset != 0 ||
  4618. AddedHeaderPage == FALSE) {
  4619. NumDataPages++;
  4620. }
  4621. }
  4622. if (PageRecord->IsImage) {
  4623. NumImagePages++;
  4624. }
  4625. }
  4626. //
  4627. // Update the information structure.
  4628. //
  4629. BasicInfo->NumDataPages += NumDataPages;
  4630. BasicInfo->NumImagePages += NumImagePages;
  4631. if (!NumImagePages && NumDataPages) {
  4632. BasicInfo->NumDataOnlySections++;
  4633. }
  4634. if (NumImagePages && (NumDataPages == 1)) {
  4635. BasicInfo->NumImageOnlySections++;
  4636. }
  4637. }
  4638. Status = STATUS_SUCCESS;
  4639. break;
  4640. case CcPfBootScenarioInformation:
  4641. //
  4642. // Check buffer size.
  4643. //
  4644. if (BufferSize < sizeof(CCPF_BOOT_SCENARIO_INFORMATION)) {
  4645. *RequiredSize = sizeof(CCPF_BOOT_SCENARIO_INFORMATION);
  4646. Status = STATUS_BUFFER_TOO_SMALL;
  4647. goto cleanup;
  4648. }
  4649. //
  4650. // Initialize return buffer.
  4651. //
  4652. BootInfo = Buffer;
  4653. RtlZeroMemory(BootInfo, sizeof(CCPF_BOOT_SCENARIO_INFORMATION));
  4654. //
  4655. // Verify that this is a boot scenario.
  4656. //
  4657. if (Scenario->ScenarioType != PfSystemBootScenarioType) {
  4658. Status = STATUS_INVALID_PARAMETER;
  4659. goto cleanup;
  4660. }
  4661. //
  4662. // Go through the scenario's sections.
  4663. //
  4664. for (SectionIdx = 0; SectionIdx < Scenario->NumSections; SectionIdx ++) {
  4665. SectionRecord = &SectionRecords[SectionIdx];
  4666. SectionName = (WCHAR *) (FileNameData + SectionRecord->FileNameOffset);
  4667. //
  4668. // Update boot phase based on section name.
  4669. //
  4670. if (SectionRecord->FileNameLength > SmssSuffixLength) {
  4671. SectionNameSuffix = SectionName + (SectionRecord->FileNameLength - SmssSuffixLength);
  4672. if (!wcscmp(SectionNameSuffix, SmssSuffix)) {
  4673. BootPhaseIdx = CcPfBootScenSubsystemInitPhase;
  4674. }
  4675. }
  4676. if (SectionRecord->FileNameLength > WinlogonSuffixLength) {
  4677. SectionNameSuffix = SectionName + (SectionRecord->FileNameLength - WinlogonSuffixLength);
  4678. if (!wcscmp(SectionNameSuffix, WinlogonSuffix)) {
  4679. BootPhaseIdx = CcPfBootScenSystemProcInitPhase;
  4680. }
  4681. }
  4682. if (SectionRecord->FileNameLength > SvchostSuffixLength) {
  4683. SectionNameSuffix = SectionName + (SectionRecord->FileNameLength - SvchostSuffixLength);
  4684. if (!wcscmp(SectionNameSuffix, SvchostSuffix)) {
  4685. BootPhaseIdx = CcPfBootScenServicesInitPhase;
  4686. }
  4687. }
  4688. if (SectionRecord->FileNameLength > UserinitSuffixLength) {
  4689. SectionNameSuffix = SectionName + (SectionRecord->FileNameLength - UserinitSuffixLength);
  4690. if (!wcscmp(SectionNameSuffix, UserinitSuffix)) {
  4691. BootPhaseIdx = CcPfBootScenUserInitPhase;
  4692. }
  4693. }
  4694. CCPF_ASSERT(BootPhaseIdx < CcPfBootScenMaxPhase);
  4695. //
  4696. // Skip this section if it was marked ignore for some reason.
  4697. //
  4698. if (SectionRecord->IsIgnore) {
  4699. continue;
  4700. }
  4701. //
  4702. // Note that we will prefetch the header page as a data
  4703. // page if this section will be prefetched as image.
  4704. //
  4705. if (SectionRecord->IsImage) {
  4706. BootInfo->NumDataPages[BootPhaseIdx]++;
  4707. AddedHeaderPage = TRUE;
  4708. } else {
  4709. AddedHeaderPage = FALSE;
  4710. }
  4711. //
  4712. // Go through the section's pages.
  4713. //
  4714. PageIdx = SectionRecord->FirstPageIdx;
  4715. while (PageIdx != PF_INVALID_PAGE_IDX) {
  4716. PageRecord = &PageRecords[PageIdx];
  4717. //
  4718. // Get the index for the next page in the list.
  4719. //
  4720. PageIdx = PageRecord->NextPageIdx;
  4721. //
  4722. // Skip pages we have marked "ignore" for some reason.
  4723. //
  4724. if (PageRecord->IsIgnore) {
  4725. continue;
  4726. }
  4727. if (PageRecord->IsData) {
  4728. //
  4729. // If this page is the first page, count it only
  4730. // if we have not already counted the header page
  4731. // for image mapping.
  4732. //
  4733. if (PageRecord->FileOffset != 0 ||
  4734. AddedHeaderPage == FALSE) {
  4735. BootInfo->NumDataPages[BootPhaseIdx]++;
  4736. }
  4737. }
  4738. if (PageRecord->IsImage) {
  4739. BootInfo->NumImagePages[BootPhaseIdx]++;
  4740. }
  4741. }
  4742. }
  4743. Status = STATUS_SUCCESS;
  4744. break;
  4745. default:
  4746. Status = STATUS_NOT_SUPPORTED;
  4747. }
  4748. //
  4749. // Fall through with status from the switch statement.
  4750. //
  4751. cleanup:
  4752. DBGPR((CCPFID,PFTRC,"CCPF: QueryScenario(%p,%x)=%x\n",Scenario,InformationType,Status));
  4753. return Status;
  4754. }
  4755. NTSTATUS
  4756. CcPfOpenVolumesForPrefetch (
  4757. IN PCCPF_PREFETCH_HEADER PrefetchHeader
  4758. )
  4759. /*++
  4760. Routine Description:
  4761. This routine is called on an initialized PrefetchHeader with the scenario
  4762. field specified. It opens the volumes specified in the scenario updating
  4763. VolumeNodes and the list of volumes we can't prefetch from and the list
  4764. of volumes we have successfully opened and saved a handle for.
  4765. Arguments:
  4766. PrefetchHeader - Pointer to prefetch header that contains the
  4767. prefetch instructions.
  4768. Return Value:
  4769. Status.
  4770. Environment:
  4771. Kernel mode, IRQL == PASSIVE_LEVEL.
  4772. --*/
  4773. {
  4774. LARGE_INTEGER CreationTime;
  4775. PCHAR MetadataInfoBase;
  4776. PPF_METADATA_RECORD MetadataRecordTable;
  4777. PPF_METADATA_RECORD MetadataRecord;
  4778. PWCHAR VolumePath;
  4779. PPF_SCENARIO_HEADER Scenario;
  4780. PCCPF_PREFETCH_VOLUME_INFO VolumeNode;
  4781. HANDLE VolumeHandle;
  4782. ULONG SerialNumber;
  4783. ULONG MetadataRecordIdx;
  4784. ULONG AllocationSize;
  4785. NTSTATUS Status;
  4786. BOOLEAN VolumeMounted;
  4787. //
  4788. // Initialize locals.
  4789. //
  4790. Scenario = PrefetchHeader->Scenario;
  4791. DBGPR((CCPFID,PFPREF,"CCPF: OpenVolumesForPrefetch(%p)\n",PrefetchHeader));
  4792. //
  4793. // Verify parameters.
  4794. //
  4795. if (Scenario == NULL) {
  4796. CCPF_ASSERT(Scenario);
  4797. Status = STATUS_INVALID_PARAMETER;
  4798. goto cleanup;
  4799. }
  4800. //
  4801. // Allocate volume nodes.
  4802. //
  4803. AllocationSize = Scenario->NumMetadataRecords * sizeof(CCPF_PREFETCH_VOLUME_INFO);
  4804. PrefetchHeader->VolumeNodes = ExAllocatePoolWithTag(PagedPool,
  4805. AllocationSize,
  4806. CCPF_ALLOC_VOLUME_TAG);
  4807. if (!PrefetchHeader->VolumeNodes) {
  4808. Status = STATUS_INSUFFICIENT_RESOURCES;
  4809. goto cleanup;
  4810. }
  4811. //
  4812. // Get pointer to metadata prefetch information.
  4813. //
  4814. MetadataInfoBase = (PCHAR)Scenario + Scenario->MetadataInfoOffset;
  4815. MetadataRecordTable = (PPF_METADATA_RECORD) MetadataInfoBase;
  4816. //
  4817. // Go through metadata records and build the volume nodes for prefetching.
  4818. //
  4819. for (MetadataRecordIdx = 0;
  4820. MetadataRecordIdx < Scenario->NumMetadataRecords;
  4821. MetadataRecordIdx++) {
  4822. //
  4823. // Initialize loop locals.
  4824. //
  4825. MetadataRecord = &MetadataRecordTable[MetadataRecordIdx];
  4826. VolumeHandle = NULL;
  4827. VolumePath = (PWCHAR)
  4828. (MetadataInfoBase + MetadataRecord->VolumeNameOffset);
  4829. //
  4830. // Is the volume mounted?
  4831. //
  4832. Status = CcPfIsVolumeMounted(VolumePath, &VolumeMounted);
  4833. if (!NT_SUCCESS(Status)) {
  4834. //
  4835. // Since we could not tell for sure, treat this volume as
  4836. // if it were not mounted.
  4837. //
  4838. VolumeMounted = FALSE;
  4839. }
  4840. //
  4841. // If the volume is not mounted we don't want to cause it to be
  4842. // mounted. This creates a problem especially during boot for
  4843. // clustering where a single physical disk is shared by many
  4844. // computers.
  4845. //
  4846. if (!VolumeMounted) {
  4847. Status = STATUS_VOLUME_DISMOUNTED;
  4848. goto NextVolume;
  4849. }
  4850. //
  4851. // Open the volume and get relevant information.
  4852. //
  4853. Status = CcPfQueryVolumeInfo(VolumePath,
  4854. &VolumeHandle,
  4855. &CreationTime,
  4856. &SerialNumber);
  4857. if (!NT_SUCCESS(Status)) {
  4858. goto NextVolume;
  4859. }
  4860. //
  4861. // For simplicity we save NT paths for the files to prefetch
  4862. // from. If volumes are mounted in a different order, or new ones
  4863. // are created these paths would not work:
  4864. // (e.g. \Device\HarddiskVolume2 should be \Device\HarddiskVolume3 etc.)
  4865. // Verify that such a change has not taken place.
  4866. //
  4867. if (SerialNumber != MetadataRecord->SerialNumber ||
  4868. CreationTime.QuadPart != MetadataRecord->CreationTime.QuadPart) {
  4869. Status = STATUS_REVISION_MISMATCH;
  4870. goto NextVolume;
  4871. }
  4872. Status = STATUS_SUCCESS;
  4873. NextVolume:
  4874. //
  4875. // Update the volume node we'll keep around for prefetching.
  4876. //
  4877. VolumeNode = &PrefetchHeader->VolumeNodes[MetadataRecordIdx];
  4878. VolumeNode->VolumePath = VolumePath;
  4879. VolumeNode->VolumePathLength = MetadataRecord->VolumeNameLength;
  4880. //
  4881. // If we failed to open the volume, or if it was not mounted or if
  4882. // its SerialNumber / CreationTime has changed put it in the list of
  4883. // volumes we won't prefetch from. Otherwise put it in the list of
  4884. // opened volumes so we don't have to open it again.
  4885. //
  4886. if (NT_SUCCESS(Status) && VolumeHandle) {
  4887. VolumeNode->VolumeHandle = VolumeHandle;
  4888. VolumeHandle = NULL;
  4889. InsertTailList(&PrefetchHeader->OpenedVolumeList, &VolumeNode->VolumeLink);
  4890. } else {
  4891. VolumeNode->VolumeHandle = NULL;
  4892. InsertTailList(&PrefetchHeader->BadVolumeList, &VolumeNode->VolumeLink);
  4893. }
  4894. if (VolumeHandle) {
  4895. ZwClose(VolumeHandle);
  4896. VolumeHandle = NULL;
  4897. }
  4898. }
  4899. //
  4900. // We've dealt with all the volumes in the prefetch instructions.
  4901. //
  4902. Status = STATUS_SUCCESS;
  4903. cleanup:
  4904. DBGPR((CCPFID,PFPREF,"CCPF: OpenVolumesForPrefetch(%p)=%x\n",PrefetchHeader,Status));
  4905. return Status;
  4906. }
  4907. PCCPF_PREFETCH_VOLUME_INFO
  4908. CcPfFindPrefetchVolumeInfoInList(
  4909. WCHAR *Path,
  4910. PLIST_ENTRY List
  4911. )
  4912. /*++
  4913. Routine Description:
  4914. This routine looks for the volume on which "Path" would be in the list of
  4915. volumes and returns it.
  4916. Arguments:
  4917. Path - NUL terminated path of the volume or a file/directory on the volume.
  4918. List - List of volumes to search.
  4919. Return Value:
  4920. Found volume or NULL.
  4921. Environment:
  4922. Kernel mode, IRQL == PASSIVE_LEVEL.
  4923. --*/
  4924. {
  4925. PCCPF_PREFETCH_VOLUME_INFO FoundVolume;
  4926. PCCPF_PREFETCH_VOLUME_INFO VolumeInfo;
  4927. PLIST_ENTRY NextEntry;
  4928. //
  4929. // Initialize locals.
  4930. //
  4931. FoundVolume = NULL;
  4932. //
  4933. // Walk the list.
  4934. //
  4935. for (NextEntry = List->Flink;
  4936. NextEntry != List;
  4937. NextEntry = NextEntry->Flink) {
  4938. VolumeInfo = CONTAINING_RECORD(NextEntry,
  4939. CCPF_PREFETCH_VOLUME_INFO,
  4940. VolumeLink);
  4941. if (!wcsncmp(Path, VolumeInfo->VolumePath, VolumeInfo->VolumePathLength)) {
  4942. FoundVolume = VolumeInfo;
  4943. break;
  4944. }
  4945. }
  4946. return FoundVolume;
  4947. }
  4948. NTSTATUS
  4949. CcPfGetSectionObject(
  4950. IN PUNICODE_STRING FilePath,
  4951. IN LOGICAL ImageSection,
  4952. OUT PVOID* SectionObject,
  4953. OUT PFILE_OBJECT* FileObject,
  4954. OUT HANDLE* FileHandle
  4955. )
  4956. /*++
  4957. Routine Description:
  4958. This routine ensures that a section for the specified file exists.
  4959. Arguments:
  4960. FilePath - Path to file to get section object for.
  4961. ImageSection - TRUE if we want to map as image
  4962. SectionObject - Receives the section object if successful (addref'd).
  4963. FileObject - Receives the file object if successful (addref'd).
  4964. FileHandle - Receives the file handle. We need to keep the file handle,
  4965. because otherwise non-paging I/O would stop working.
  4966. Return Value:
  4967. Status.
  4968. Environment:
  4969. Kernel mode. IRQL == PASSIVE_LEVEL.
  4970. --*/
  4971. {
  4972. HANDLE SectionHandle;
  4973. OBJECT_ATTRIBUTES ObjectAttributes;
  4974. IO_STATUS_BLOCK IoStatus;
  4975. NTSTATUS status;
  4976. ULONG SectionFlags;
  4977. ULONG SectionAccess;
  4978. ULONG FileAccess;
  4979. extern POBJECT_TYPE IoFileObjectType;
  4980. DBGPR((CCPFID,PFPRFD,"CCPF: GetSection(%wZ,%d)\n", FilePath, ImageSection));
  4981. //
  4982. // Reset parameters.
  4983. //
  4984. *SectionObject = NULL;
  4985. *FileObject = NULL;
  4986. *FileHandle = NULL;
  4987. if (!ImageSection) {
  4988. SectionFlags = SEC_RESERVE;
  4989. FileAccess = FILE_READ_DATA | FILE_READ_ATTRIBUTES;
  4990. SectionAccess = PAGE_READWRITE;
  4991. } else {
  4992. SectionFlags = SEC_IMAGE;
  4993. FileAccess = FILE_EXECUTE;
  4994. SectionAccess = PAGE_EXECUTE;
  4995. }
  4996. //
  4997. // To ensure that the section exists and is addref'd, we simply
  4998. // open the file and create a section. This way we let Io and Mm
  4999. // handle all the details.
  5000. //
  5001. InitializeObjectAttributes(&ObjectAttributes,
  5002. FilePath,
  5003. OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
  5004. NULL,
  5005. NULL);
  5006. status = IoCreateFile(FileHandle,
  5007. (ACCESS_MASK) FileAccess,
  5008. &ObjectAttributes,
  5009. &IoStatus,
  5010. NULL,
  5011. FILE_ATTRIBUTE_NORMAL,
  5012. FILE_SHARE_READ | FILE_SHARE_DELETE,
  5013. FILE_OPEN,
  5014. FILE_NON_DIRECTORY_FILE,
  5015. NULL,
  5016. 0,
  5017. CreateFileTypeNone,
  5018. (PVOID)NULL,
  5019. IO_FORCE_ACCESS_CHECK |
  5020. IO_NO_PARAMETER_CHECKING |
  5021. IO_CHECK_CREATE_PARAMETERS);
  5022. if (!NT_SUCCESS(status)) {
  5023. goto _return;
  5024. }
  5025. //
  5026. // Create section.
  5027. //
  5028. InitializeObjectAttributes(&ObjectAttributes,
  5029. NULL,
  5030. OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
  5031. NULL,
  5032. NULL);
  5033. status = ZwCreateSection(&SectionHandle,
  5034. SECTION_MAP_READ | SECTION_MAP_EXECUTE | SECTION_QUERY,
  5035. &ObjectAttributes,
  5036. NULL,
  5037. SectionAccess,
  5038. SectionFlags,
  5039. *FileHandle);
  5040. if (!NT_SUCCESS(status)) {
  5041. ZwClose(*FileHandle);
  5042. *FileHandle = NULL;
  5043. goto _return;
  5044. }
  5045. //
  5046. // Get section object pointer.
  5047. //
  5048. status = ObReferenceObjectByHandle(
  5049. SectionHandle,
  5050. SECTION_MAP_READ | SECTION_MAP_EXECUTE | SECTION_QUERY,
  5051. MmSectionObjectType,
  5052. KernelMode,
  5053. SectionObject,
  5054. NULL
  5055. );
  5056. ZwClose(SectionHandle);
  5057. if (!NT_SUCCESS(status)) {
  5058. *SectionObject = NULL;
  5059. ZwClose(*FileHandle);
  5060. *FileHandle = NULL;
  5061. goto _return;
  5062. }
  5063. //
  5064. // Get file object pointer.
  5065. //
  5066. status = ObReferenceObjectByHandle(*FileHandle,
  5067. FileAccess,
  5068. IoFileObjectType,
  5069. KernelMode,
  5070. (PVOID*)FileObject,
  5071. NULL);
  5072. if (!NT_SUCCESS(status)) {
  5073. ObDereferenceObject(*SectionObject);
  5074. *SectionObject = NULL;
  5075. *FileObject = NULL;
  5076. ZwClose(*FileHandle);
  5077. *FileHandle = NULL;
  5078. goto _return;
  5079. }
  5080. _return:
  5081. DBGPR((CCPFID,PFPRFD,"CCPF: GetSection(%wZ)=%x\n", FilePath, status));
  5082. return status;
  5083. }
  5084. //
  5085. // Routines used for application launch prefetching.
  5086. //
  5087. NTSTATUS
  5088. CcPfScanCommandLine(
  5089. OUT PULONG PrefetchHint,
  5090. OPTIONAL OUT PULONG HashId
  5091. )
  5092. /*++
  5093. Routine Description:
  5094. Scan the command line (in the PEB) for the current process.
  5095. Checks for /prefetch:XXX in the command line. This is specified by
  5096. applications to distinguish different ways they are launched in so
  5097. we can customize application launch prefetching for them (e.g. have
  5098. different prefetch instructions for Windows Media player that is
  5099. launched to play a CD than one that is launched to browse the web.
  5100. If HashId is requested, calculates a hash ID from the full command line.
  5101. Arguments:
  5102. PrefetchHint - Hint specified in the command line. If no hint is
  5103. specified, 0 will be returned.
  5104. HashId - Calculated hash id is returned here.
  5105. Return Value:
  5106. Status.
  5107. Environment:
  5108. Kernel mode. IRQL == PASSIVE_LEVEL.
  5109. --*/
  5110. {
  5111. PEPROCESS CurrentProcess;
  5112. PPEB Peb;
  5113. PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
  5114. PWCHAR FoundPosition;
  5115. PWCHAR Source;
  5116. PWCHAR SourceEnd;
  5117. PWCHAR Destination;
  5118. PWCHAR DestinationEnd;
  5119. UNICODE_STRING CommandLine;
  5120. UNICODE_STRING PrefetchParameterName;
  5121. NTSTATUS Status;
  5122. ULONG PrefetchHintStringMaxChars;
  5123. WCHAR PrefetchHintString[15];
  5124. //
  5125. // Initialize locals.
  5126. //
  5127. RtlInitUnicodeString(&PrefetchParameterName, L"/prefetch:");
  5128. PrefetchHintStringMaxChars = sizeof(PrefetchHintString) / sizeof(PrefetchHintString[0]);
  5129. CurrentProcess = PsGetCurrentProcess();
  5130. Peb = CurrentProcess->Peb;
  5131. //
  5132. // Initialize output parameters.
  5133. //
  5134. *PrefetchHint = 0;
  5135. //
  5136. // Make sure the user mode process environment block is not gone.
  5137. //
  5138. if (!Peb) {
  5139. Status = STATUS_TOO_LATE;
  5140. goto cleanup;
  5141. }
  5142. try {
  5143. //
  5144. // Make sure we can access the process parameters structure.
  5145. //
  5146. ProcessParameters = Peb->ProcessParameters;
  5147. ProbeForReadSmallStructure(ProcessParameters,
  5148. sizeof(*ProcessParameters),
  5149. _alignof(RTL_USER_PROCESS_PARAMETERS));
  5150. //
  5151. // Copy CommandLine UNICODE_STRING structure to a local.
  5152. //
  5153. CommandLine = ProcessParameters->CommandLine;
  5154. //
  5155. // Is there a command line?
  5156. //
  5157. if (!CommandLine.Buffer) {
  5158. Status = STATUS_NOT_FOUND;
  5159. goto cleanup;
  5160. }
  5161. //
  5162. // If ProcessParameters has been de-normalized, normalize CommandLine.
  5163. //
  5164. if ((ProcessParameters->Flags & RTL_USER_PROC_PARAMS_NORMALIZED) == 0) {
  5165. CommandLine.Buffer = (PWSTR)((PCHAR)ProcessParameters + (ULONG_PTR) CommandLine.Buffer);
  5166. }
  5167. //
  5168. // Probe the command line string.
  5169. //
  5170. ProbeForRead(CommandLine.Buffer, CommandLine.Length, _alignof(WCHAR));
  5171. //
  5172. // Look for the prefetch hint parameter.
  5173. //
  5174. FoundPosition = CcPfFindString(&CommandLine, &PrefetchParameterName);
  5175. if (FoundPosition) {
  5176. //
  5177. // Copy the decimal number following the prefetch hint switch into
  5178. // our local buffer and NUL terminate it.
  5179. //
  5180. Source = FoundPosition + (PrefetchParameterName.Length / sizeof(WCHAR));
  5181. SourceEnd = CommandLine.Buffer + (CommandLine.Length / sizeof(WCHAR));
  5182. Destination = PrefetchHintString;
  5183. DestinationEnd = PrefetchHintString + PrefetchHintStringMaxChars - 1;
  5184. //
  5185. // Copy while we don't hit the end of the command line string and the
  5186. // end of our local buffer (we left room for a terminating NUL), and
  5187. // we don't hit a space (' ') that would mark the end of the prefetch
  5188. // hint command line parameter.
  5189. //
  5190. while ((Source < SourceEnd) &&
  5191. (Destination < DestinationEnd) &&
  5192. (*Source != L' ')) {
  5193. *Destination = *Source;
  5194. Source++;
  5195. Destination++;
  5196. }
  5197. //
  5198. // Terminate prefetch hint string. DestinationEnd is the last
  5199. // character within the PrefetchHintString bounds. Destination
  5200. // can only be <= DestinationEnd.
  5201. //
  5202. CCPF_ASSERT(Destination <= DestinationEnd);
  5203. *Destination = 0;
  5204. //
  5205. // Convert prefetch hint to a number.
  5206. //
  5207. *PrefetchHint = _wtol(PrefetchHintString);
  5208. }
  5209. //
  5210. // Calculate hash id.
  5211. //
  5212. if (HashId) {
  5213. *HashId = CcPfHashValue(CommandLine.Buffer, CommandLine.Length);
  5214. }
  5215. //
  5216. // We are done.
  5217. //
  5218. Status = STATUS_SUCCESS;
  5219. } except (EXCEPTION_EXECUTE_HANDLER) {
  5220. Status = GetExceptionCode();
  5221. CCPF_ASSERT(!NT_SUCCESS(Status));
  5222. }
  5223. //
  5224. // Fall through with the status.
  5225. //
  5226. cleanup:
  5227. return Status;
  5228. }
  5229. //
  5230. // Reference count implementation:
  5231. //
  5232. VOID
  5233. CcPfInitializeRefCount(
  5234. PCCPF_REFCOUNT RefCount
  5235. )
  5236. /*++
  5237. Routine Description:
  5238. This routine initializes a reference count structure.
  5239. Arguments:
  5240. RefCount - Pointer to reference count structure.
  5241. Return Value:
  5242. None.
  5243. Environment:
  5244. Kernel Mode, IRQL == PASSIVE_LEVEL.
  5245. --*/
  5246. {
  5247. //
  5248. // Start reference count from 1. When somebody wants to gain
  5249. // exclusive access they decrement it one extra so it may become
  5250. // 0.
  5251. //
  5252. RefCount->RefCount = 1;
  5253. //
  5254. // Nobody has exclusive access to start with.
  5255. //
  5256. RefCount->Exclusive = 0;
  5257. }
  5258. NTSTATUS
  5259. FASTCALL
  5260. CcPfAddRef(
  5261. PCCPF_REFCOUNT RefCount
  5262. )
  5263. /*++
  5264. Routine Description:
  5265. This routine tries to bump the reference count if it has not been
  5266. acquired exclusive.
  5267. Arguments:
  5268. RefCount - Pointer to reference count structure.
  5269. Return Value:
  5270. Status.
  5271. Environment:
  5272. Kernel Mode, IRQL <= DISPATCH_LEVEL if RefCount is non-paged.
  5273. --*/
  5274. {
  5275. //
  5276. // Do a fast check if the lock was acquire exclusive. If so just
  5277. // return.
  5278. //
  5279. if (RefCount->Exclusive) {
  5280. return STATUS_UNSUCCESSFUL;
  5281. }
  5282. //
  5283. // Bump the reference count.
  5284. //
  5285. InterlockedIncrement(&RefCount->RefCount);
  5286. //
  5287. // If it was acquired exclusive, pull back.
  5288. //
  5289. if (RefCount->Exclusive) {
  5290. InterlockedDecrement(&RefCount->RefCount);
  5291. //
  5292. // Reference count should never go negative.
  5293. //
  5294. CCPF_ASSERT(RefCount->RefCount >= 0);
  5295. return STATUS_UNSUCCESSFUL;
  5296. } else {
  5297. //
  5298. // We got our reference.
  5299. //
  5300. return STATUS_SUCCESS;
  5301. }
  5302. }
  5303. VOID
  5304. FASTCALL
  5305. CcPfDecRef(
  5306. PCCPF_REFCOUNT RefCount
  5307. )
  5308. /*++
  5309. Routine Description:
  5310. This routine decrements the reference count.
  5311. Arguments:
  5312. RefCount - Pointer to reference count structure.
  5313. Return Value:
  5314. None.
  5315. Environment:
  5316. Kernel Mode, IRQL <= DISPATCH_LEVEL if RefCount is non-paged.
  5317. --*/
  5318. {
  5319. //
  5320. // Decrement the reference count.
  5321. //
  5322. InterlockedDecrement(&RefCount->RefCount);
  5323. //
  5324. // Reference count should never go negative.
  5325. //
  5326. CCPF_ASSERT(RefCount->RefCount >= 0);
  5327. }
  5328. NTSTATUS
  5329. FASTCALL
  5330. CcPfAddRefEx(
  5331. PCCPF_REFCOUNT RefCount,
  5332. ULONG Count
  5333. )
  5334. /*++
  5335. Routine Description:
  5336. This routine tries to bump the reference count if it has not been
  5337. acquired exclusive.
  5338. Arguments:
  5339. RefCount - Pointer to reference count structure.
  5340. Count - Amount to bump the reference count by
  5341. Return Value:
  5342. Status.
  5343. Environment:
  5344. Kernel Mode, IRQL <= DISPATCH_LEVEL if RefCount is non-paged.
  5345. --*/
  5346. {
  5347. //
  5348. // Do a fast check if the lock was acquire exclusive. If so just
  5349. // return.
  5350. //
  5351. if (RefCount->Exclusive) {
  5352. return STATUS_UNSUCCESSFUL;
  5353. }
  5354. //
  5355. // Bump the reference count.
  5356. //
  5357. InterlockedExchangeAdd(&RefCount->RefCount, Count);
  5358. //
  5359. // If it was acquired exclusive, pull back.
  5360. //
  5361. if (RefCount->Exclusive) {
  5362. InterlockedExchangeAdd(&RefCount->RefCount, -(LONG) Count);
  5363. //
  5364. // Reference count should never go negative.
  5365. //
  5366. CCPF_ASSERT(RefCount->RefCount >= 0);
  5367. return STATUS_UNSUCCESSFUL;
  5368. } else {
  5369. //
  5370. // We got our reference.
  5371. //
  5372. return STATUS_SUCCESS;
  5373. }
  5374. }
  5375. VOID
  5376. FASTCALL
  5377. CcPfDecRefEx(
  5378. PCCPF_REFCOUNT RefCount,
  5379. ULONG Count
  5380. )
  5381. /*++
  5382. Routine Description:
  5383. This routine decrements the reference count.
  5384. Arguments:
  5385. RefCount - Pointer to reference count structure.
  5386. Count - Count of how far to decrement the reference count by
  5387. Return Value:
  5388. None.
  5389. Environment:
  5390. Kernel Mode, IRQL <= DISPATCH_LEVEL if RefCount is non-paged.
  5391. --*/
  5392. {
  5393. //
  5394. // Decrement the reference count.
  5395. //
  5396. InterlockedExchangeAdd(&RefCount->RefCount, -(LONG) Count);
  5397. //
  5398. // Reference count should never go negative.
  5399. //
  5400. CCPF_ASSERT(RefCount->RefCount >= 0);
  5401. }
  5402. NTSTATUS
  5403. CcPfAcquireExclusiveRef(
  5404. PCCPF_REFCOUNT RefCount
  5405. )
  5406. /*++
  5407. Routine Description:
  5408. This routine attempts to get exclusive reference. If there is
  5409. already an exclusive reference, it fails. Othwerwise it waits for
  5410. all normal references to go away.
  5411. Arguments:
  5412. RefCount - Pointer to reference count structure.
  5413. Return Value:
  5414. Status.
  5415. Environment:
  5416. Kernel Mode, IRQL == PASSIVE_LEVEL.
  5417. --*/
  5418. {
  5419. LONG OldValue;
  5420. LARGE_INTEGER SleepTime;
  5421. //
  5422. // Try to get exclusive access by setting Exclusive from 0 to 1.
  5423. //
  5424. OldValue = InterlockedCompareExchange(&RefCount->Exclusive, 1, 0);
  5425. if (OldValue != 0) {
  5426. //
  5427. // Somebody already had the lock.
  5428. //
  5429. return STATUS_UNSUCCESSFUL;
  5430. }
  5431. //
  5432. // Decrement the reference count once so it may become 0.
  5433. //
  5434. InterlockedDecrement(&RefCount->RefCount);
  5435. //
  5436. // No new references will be given away. We poll until existing
  5437. // references are released.
  5438. //
  5439. do {
  5440. if (RefCount->RefCount == 0) {
  5441. break;
  5442. } else {
  5443. //
  5444. // Sleep for a while [in 100ns, negative so it is relative
  5445. // to current system time].
  5446. //
  5447. SleepTime.QuadPart = - 10 * 1000 * 10; // 10 ms.
  5448. KeDelayExecutionThread(KernelMode, FALSE, &SleepTime);
  5449. }
  5450. } while(TRUE);
  5451. return STATUS_SUCCESS;
  5452. }
  5453. PCCPF_TRACE_HEADER
  5454. CcPfReferenceProcessTrace(
  5455. PEPROCESS Process
  5456. )
  5457. /*++
  5458. Routine Description:
  5459. This routine references the trace associated with the specified process
  5460. if possible. It uses fast references to avoid taking the trace lock
  5461. to improve performance.
  5462. Arguments:
  5463. Process - The process whose trace should be referenced
  5464. Return Value:
  5465. The referenced trace buffer or NULL if it could not be referenced
  5466. --*/
  5467. {
  5468. EX_FAST_REF OldRef;
  5469. PCCPF_TRACE_HEADER Trace;
  5470. ULONG RefsToAdd, Unused;
  5471. NTSTATUS Status;
  5472. KIRQL OldIrql;
  5473. //
  5474. // Attempt the fast reference
  5475. //
  5476. OldRef = ExFastReference (&Process->PrefetchTrace);
  5477. Trace = ExFastRefGetObject (OldRef);
  5478. //
  5479. // Optimize the common path where there won't be a trace on the
  5480. // process header (since traces are just for the application launch.)
  5481. //
  5482. if (Trace == NULL) {
  5483. return 0;
  5484. }
  5485. //
  5486. // We fail if there wasn't a trace or if it has no cached references
  5487. // left. Both of these cases had the cached reference count zero.
  5488. //
  5489. Unused = ExFastRefGetUnusedReferences (OldRef);
  5490. if (Unused <= 1) {
  5491. //
  5492. // If there are no references left then we have to do this under the lock
  5493. //
  5494. if (Unused == 0) {
  5495. Status = STATUS_SUCCESS;
  5496. KeAcquireSpinLock(&CcPfGlobals.ActiveTracesLock, &OldIrql);
  5497. Trace = ExFastRefGetObject (Process->PrefetchTrace);
  5498. if (Trace != NULL) {
  5499. Status = CcPfAddRef(&Trace->RefCount);
  5500. }
  5501. KeReleaseSpinLock(&CcPfGlobals.ActiveTracesLock, OldIrql);
  5502. if (!NT_SUCCESS (Status)) {
  5503. Trace = NULL;
  5504. }
  5505. return Trace;
  5506. }
  5507. //
  5508. // If we took the counter to zero then attempt to make life easier for
  5509. // the next referencer by resetting the counter to its max. Since we now
  5510. // have a reference to the object we can do this.
  5511. //
  5512. RefsToAdd = ExFastRefGetAdditionalReferenceCount ();
  5513. Status = CcPfAddRefEx (&Trace->RefCount, RefsToAdd);
  5514. //
  5515. // If we failed to obtain additional references then just ignore the fixup.
  5516. //
  5517. if (NT_SUCCESS (Status)) {
  5518. //
  5519. // If we fail to add them to the fast reference structure then
  5520. // give them back to the trace and forget about fixup.
  5521. //
  5522. if (!ExFastRefAddAdditionalReferenceCounts (&Process->PrefetchTrace, Trace, RefsToAdd)) {
  5523. CcPfDecRefEx (&Trace->RefCount, RefsToAdd);
  5524. }
  5525. }
  5526. }
  5527. return Trace;
  5528. }
  5529. PCCPF_TRACE_HEADER
  5530. CcPfRemoveProcessTrace(
  5531. PEPROCESS Process
  5532. )
  5533. /*++
  5534. Routine Description:
  5535. This routine removes the trace associated with the specified process.
  5536. It returns the trace with the original reference acquired by AddProcessTrace.
  5537. Arguments:
  5538. Process - The process whose trace should be removed
  5539. Return Value:
  5540. The removed trace buffer.
  5541. --*/
  5542. {
  5543. EX_FAST_REF OldRef;
  5544. PCCPF_TRACE_HEADER Trace;
  5545. ULONG RefsToReturn;
  5546. KIRQL OldIrql;
  5547. //
  5548. // Do the swap.
  5549. //
  5550. OldRef = ExFastRefSwapObject (&Process->PrefetchTrace, NULL);
  5551. Trace = ExFastRefGetObject (OldRef);
  5552. //
  5553. // We should have a trace on the process if we are trying to remove it.
  5554. //
  5555. CCPF_ASSERT(Trace);
  5556. //
  5557. // Work out how many cached references there were (if any) and
  5558. // return them.
  5559. //
  5560. RefsToReturn = ExFastRefGetUnusedReferences (OldRef);
  5561. if (RefsToReturn > 0) {
  5562. CcPfDecRefEx (&Trace->RefCount, RefsToReturn);
  5563. }
  5564. //
  5565. // Force any slow path references out of that path now before we return
  5566. // the trace.
  5567. //
  5568. #if !defined (NT_UP)
  5569. KeAcquireSpinLock(&CcPfGlobals.ActiveTracesLock, &OldIrql);
  5570. KeReleaseSpinLock(&CcPfGlobals.ActiveTracesLock, OldIrql);
  5571. #endif // NT_UP
  5572. //
  5573. // We are returning the trace with the extra reference we had acquired in
  5574. // AddProcessTrace.
  5575. //
  5576. return Trace;
  5577. }
  5578. NTSTATUS
  5579. CcPfAddProcessTrace(
  5580. PEPROCESS Process,
  5581. PCCPF_TRACE_HEADER Trace
  5582. )
  5583. /*++
  5584. Routine Description:
  5585. This routine adds the trace associated with the specified process
  5586. if possible.
  5587. Arguments:
  5588. Process - The process whose trace should be removed
  5589. Trace - The trace to associate with the process
  5590. Return Value:
  5591. Status.
  5592. --*/
  5593. {
  5594. NTSTATUS Status;
  5595. //
  5596. // Bias the trace reference by the cache size + an additional reference to
  5597. // be associated with the fast reference as a whole (allowing the slow
  5598. // path to access the trace.)
  5599. //
  5600. Status = CcPfAddRefEx (&Trace->RefCount, ExFastRefGetAdditionalReferenceCount () + 1);
  5601. if (NT_SUCCESS (Status)) {
  5602. ExFastRefInitialize (&Process->PrefetchTrace, Trace);
  5603. }
  5604. return Status;
  5605. }
  5606. //
  5607. // Utility routines.
  5608. //
  5609. PWCHAR
  5610. CcPfFindString (
  5611. PUNICODE_STRING SearchIn,
  5612. PUNICODE_STRING SearchFor
  5613. )
  5614. /*++
  5615. Routine Description:
  5616. Finds SearchFor string in SearchIn string and returns pointer to the
  5617. beginning of the match in SearchIn.
  5618. Arguments:
  5619. SearchIn - Pointer to string to search in.
  5620. SearchFor - Pointer to string to search for.
  5621. Return Value:
  5622. Pointer to beginning of match in SearchIn, or NULL if not found.
  5623. Environment:
  5624. Kernel mode, IRQL <= DISPATCH_LEVEL if *Key is NonPaged.
  5625. --*/
  5626. {
  5627. PWCHAR SearchInPosition;
  5628. PWCHAR SearchInEnd;
  5629. PWCHAR SearchInMatchPosition;
  5630. PWCHAR SearchForPosition;
  5631. PWCHAR SearchForEnd;
  5632. SearchInPosition = SearchIn->Buffer;
  5633. SearchInEnd = SearchIn->Buffer + (SearchIn->Length / sizeof(WCHAR));
  5634. SearchForEnd = SearchFor->Buffer + (SearchFor->Length / sizeof(WCHAR));
  5635. while (SearchInPosition < SearchInEnd) {
  5636. //
  5637. // Try to match the SearchFor string starting at SearchInPosition.
  5638. //
  5639. SearchInMatchPosition = SearchInPosition;
  5640. SearchForPosition = SearchFor->Buffer;
  5641. while ((SearchInMatchPosition < SearchInEnd) &&
  5642. (SearchForPosition < SearchForEnd) &&
  5643. (*SearchInMatchPosition == *SearchForPosition)) {
  5644. SearchInMatchPosition++;
  5645. SearchForPosition++;
  5646. }
  5647. //
  5648. // We should not go beyond bounds.
  5649. //
  5650. CCPF_ASSERT(SearchInMatchPosition <= SearchInEnd);
  5651. CCPF_ASSERT(SearchForPosition <= SearchForEnd);
  5652. //
  5653. // If we matched up to the end of SearchFor string, we found it.
  5654. //
  5655. if (SearchForPosition == SearchForEnd) {
  5656. return SearchInPosition;
  5657. }
  5658. //
  5659. // Look for a match starting at the next character in the SearchIn string.
  5660. //
  5661. SearchInPosition++;
  5662. }
  5663. //
  5664. // We could not find the SearchFor string in SearchIn string.
  5665. //
  5666. return NULL;
  5667. }
  5668. ULONG
  5669. CcPfHashValue(
  5670. PVOID key,
  5671. ULONG len
  5672. )
  5673. /*++
  5674. Routine Description:
  5675. Generic hash routine.
  5676. Arguments:
  5677. Key - Pointer to data to calculate a hash value for.
  5678. Len - Number of bytes pointed to by key.
  5679. Return Value:
  5680. Hash value.
  5681. Environment:
  5682. Kernel mode, IRQL <= DISPATCH_LEVEL if *Key is NonPaged.
  5683. --*/
  5684. {
  5685. char *cp = key;
  5686. ULONG i, convkey=0;
  5687. for(i = 0; i < len; i++)
  5688. {
  5689. convkey = 37 * convkey + (unsigned int) *cp;
  5690. cp++;
  5691. }
  5692. #define CCPF_RNDM_CONSTANT 314159269
  5693. #define CCPF_RNDM_PRIME 1000000007
  5694. return (abs(CCPF_RNDM_CONSTANT * convkey) % CCPF_RNDM_PRIME);
  5695. }
  5696. NTSTATUS
  5697. CcPfIsVolumeMounted (
  5698. IN WCHAR *VolumePath,
  5699. OUT BOOLEAN *VolumeMounted
  5700. )
  5701. /*++
  5702. Routine Description:
  5703. Determines if the volume is mounted without causing it to be
  5704. mounted..
  5705. Arguments:
  5706. VolumePath - Pointer to NUL terminated volume path.
  5707. VolumeMounted - Whether the volume mounted is returned here.
  5708. Return Value:
  5709. Status.
  5710. Environment:
  5711. Kernel mode. IRQL == PASSIVE_LEVEL.
  5712. --*/
  5713. {
  5714. HANDLE VolumeHandle;
  5715. FILE_FS_DEVICE_INFORMATION DeviceInfo;
  5716. UNICODE_STRING VolumePathU;
  5717. OBJECT_ATTRIBUTES ObjectAttributes;
  5718. IO_STATUS_BLOCK IoStatusBlock;
  5719. NTSTATUS Status;
  5720. BOOLEAN OpenedVolume;
  5721. //
  5722. // Initialize locals.
  5723. //
  5724. OpenedVolume = FALSE;
  5725. //
  5726. // Open the device so we can query if a volume is mounted without
  5727. // causing it to be mounted.
  5728. //
  5729. RtlInitUnicodeString(&VolumePathU, VolumePath);
  5730. InitializeObjectAttributes(&ObjectAttributes,
  5731. &VolumePathU,
  5732. OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
  5733. NULL,
  5734. NULL);
  5735. Status = ZwCreateFile(&VolumeHandle,
  5736. FILE_READ_ATTRIBUTES | SYNCHRONIZE,
  5737. &ObjectAttributes,
  5738. &IoStatusBlock,
  5739. 0,
  5740. 0,
  5741. FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
  5742. FILE_OPEN,
  5743. FILE_SYNCHRONOUS_IO_NONALERT,
  5744. NULL,
  5745. 0);
  5746. if (!NT_SUCCESS(Status)) {
  5747. goto cleanup;
  5748. }
  5749. OpenedVolume = TRUE;
  5750. //
  5751. // Make the device info query.
  5752. //
  5753. Status = ZwQueryVolumeInformationFile(VolumeHandle,
  5754. &IoStatusBlock,
  5755. &DeviceInfo,
  5756. sizeof(DeviceInfo),
  5757. FileFsDeviceInformation);
  5758. if (NT_ERROR(Status)) {
  5759. goto cleanup;
  5760. }
  5761. //
  5762. // Is a volume mounted on this device?
  5763. //
  5764. *VolumeMounted = (DeviceInfo.Characteristics & FILE_DEVICE_IS_MOUNTED) ? TRUE : FALSE;
  5765. Status = STATUS_SUCCESS;
  5766. cleanup:
  5767. if (OpenedVolume) {
  5768. ZwClose(VolumeHandle);
  5769. }
  5770. return Status;
  5771. }
  5772. NTSTATUS
  5773. CcPfQueryVolumeInfo (
  5774. IN WCHAR *VolumePath,
  5775. OPTIONAL OUT HANDLE *VolumeHandleOut,
  5776. OUT PLARGE_INTEGER CreationTime,
  5777. OUT PULONG SerialNumber
  5778. )
  5779. /*++
  5780. Routine Description:
  5781. Queries volume information for the specified volume.
  5782. Arguments:
  5783. VolumePath - Pointer to NUL terminated volume path.
  5784. VolumeHandleOut - If specified, the volume handle is returned here.
  5785. The caller has to close the volume when done with it.
  5786. VolumeMounted - If specified, first we check the volume is already
  5787. mounted. If volume is not mounted we don't query anything else.
  5788. CreationTime - Pointer to where creation time of the volume will
  5789. be put.
  5790. SerialNumber - Pointer to where serial number of the volume will be put.
  5791. Return Value:
  5792. Status.
  5793. Environment:
  5794. Kernel mode. IRQL == PASSIVE_LEVEL.
  5795. --*/
  5796. {
  5797. HANDLE VolumeHandle;
  5798. FILE_FS_VOLUME_INFORMATION VolumeInfo;
  5799. UNICODE_STRING VolumePathU;
  5800. OBJECT_ATTRIBUTES ObjectAttributes;
  5801. IO_STATUS_BLOCK IoStatusBlock;
  5802. NTSTATUS Status;
  5803. BOOLEAN OpenedVolume;
  5804. //
  5805. // Initialize locals.
  5806. //
  5807. OpenedVolume = FALSE;
  5808. //
  5809. // Open the volume so we can make queries to the file system
  5810. // mounted on it. This will cause a mount if the volume has not been
  5811. // mounted.
  5812. //
  5813. RtlInitUnicodeString(&VolumePathU, VolumePath);
  5814. InitializeObjectAttributes(&ObjectAttributes,
  5815. &VolumePathU,
  5816. OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
  5817. NULL,
  5818. NULL);
  5819. Status = ZwCreateFile(&VolumeHandle,
  5820. FILE_WRITE_ATTRIBUTES | FILE_READ_ATTRIBUTES | SYNCHRONIZE,
  5821. &ObjectAttributes,
  5822. &IoStatusBlock,
  5823. 0,
  5824. 0,
  5825. FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
  5826. FILE_OPEN,
  5827. FILE_SYNCHRONOUS_IO_NONALERT,
  5828. NULL,
  5829. 0);
  5830. if (!NT_SUCCESS(Status)) {
  5831. goto cleanup;
  5832. }
  5833. OpenedVolume = TRUE;
  5834. //
  5835. // Query volume information. We won't have space for the full
  5836. // volume label in our buffer but we don't really need it. The
  5837. // file systems seem to fill in the SerialNo/CreationTime fields
  5838. // and return a STATUS_MORE_DATA warning status.
  5839. //
  5840. Status = ZwQueryVolumeInformationFile(VolumeHandle,
  5841. &IoStatusBlock,
  5842. &VolumeInfo,
  5843. sizeof(VolumeInfo),
  5844. FileFsVolumeInformation);
  5845. if (NT_ERROR(Status)) {
  5846. goto cleanup;
  5847. }
  5848. *CreationTime = VolumeInfo.VolumeCreationTime;
  5849. *SerialNumber = VolumeInfo.VolumeSerialNumber;
  5850. Status = STATUS_SUCCESS;
  5851. cleanup:
  5852. if (NT_SUCCESS(Status)) {
  5853. //
  5854. // If the caller wants the volume handle, hand it over to them.
  5855. // It is their responsibility to close the handle.
  5856. //
  5857. if (VolumeHandleOut) {
  5858. *VolumeHandleOut = VolumeHandle;
  5859. OpenedVolume = FALSE;
  5860. }
  5861. }
  5862. if (OpenedVolume) {
  5863. ZwClose(VolumeHandle);
  5864. }
  5865. return Status;
  5866. }
  5867. //
  5868. // Verification code shared between the kernel and user mode
  5869. // components. This code should be kept in sync with a simple copy &
  5870. // paste, so don't add any kernel/user specific code/macros. Note that
  5871. // the prefix on the function names are Pf, just like it is with
  5872. // shared structures / constants.
  5873. //
  5874. BOOLEAN
  5875. PfWithinBounds(
  5876. PVOID Pointer,
  5877. PVOID Base,
  5878. ULONG Length
  5879. )
  5880. /*++
  5881. Routine Description:
  5882. Check whether the pointer is within Length bytes from the base.
  5883. Arguments:
  5884. Pointer - Pointer to check.
  5885. Base - Pointer to base of mapping/array etc.
  5886. Length - Number of bytes that are valid starting from Base.
  5887. Return Value:
  5888. TRUE - Pointer is within bounds.
  5889. FALSE - Pointer is not within bounds.
  5890. --*/
  5891. {
  5892. if (((PCHAR)Pointer < (PCHAR)Base) ||
  5893. ((PCHAR)Pointer >= ((PCHAR)Base + Length))) {
  5894. return FALSE;
  5895. } else {
  5896. return TRUE;
  5897. }
  5898. }
  5899. BOOLEAN
  5900. PfVerifyScenarioId (
  5901. PPF_SCENARIO_ID ScenarioId
  5902. )
  5903. /*++
  5904. Routine Description:
  5905. Verify that the scenario id is sensible.
  5906. Arguments:
  5907. ScenarioId - Scenario Id to verify.
  5908. Return Value:
  5909. TRUE - ScenarioId is fine.
  5910. FALSE - ScenarioId is corrupt.
  5911. --*/
  5912. {
  5913. LONG CurCharIdx;
  5914. //
  5915. // Make sure the scenario name is NUL terminated.
  5916. //
  5917. for (CurCharIdx = PF_SCEN_ID_MAX_CHARS; CurCharIdx >= 0; CurCharIdx--) {
  5918. if (ScenarioId->ScenName[CurCharIdx] == 0) {
  5919. break;
  5920. }
  5921. }
  5922. if (ScenarioId->ScenName[CurCharIdx] != 0) {
  5923. return FALSE;
  5924. }
  5925. //
  5926. // Make sure there is a scenario name.
  5927. //
  5928. if (CurCharIdx == 0) {
  5929. return FALSE;
  5930. }
  5931. //
  5932. // Checks passed.
  5933. //
  5934. return TRUE;
  5935. }
  5936. BOOLEAN
  5937. PfVerifyScenarioBuffer(
  5938. PPF_SCENARIO_HEADER Scenario,
  5939. ULONG BufferSize,
  5940. PULONG FailedCheck
  5941. )
  5942. /*++
  5943. Routine Description:
  5944. Verify offset and indices in a scenario file are not beyond
  5945. bounds. This code is shared between the user mode service and
  5946. kernel mode component. If you update this function, update it in
  5947. both.
  5948. Arguments:
  5949. Scenario - Base of mapped view of the whole file.
  5950. BufferSize - Size of the scenario buffer.
  5951. FailedCheck - If verify failed, Id for the check that was failed.
  5952. Return Value:
  5953. TRUE - Scenario is fine.
  5954. FALSE - Scenario is corrupt.
  5955. --*/
  5956. {
  5957. PPF_SECTION_RECORD Sections;
  5958. PPF_SECTION_RECORD pSection;
  5959. ULONG SectionIdx;
  5960. PPF_PAGE_RECORD Pages;
  5961. PPF_PAGE_RECORD pPage;
  5962. LONG PageIdx;
  5963. PCHAR FileNames;
  5964. PCHAR pFileNameStart;
  5965. PCHAR pFileNameEnd;
  5966. PWCHAR pwFileName;
  5967. LONG FailedCheckId;
  5968. ULONG NumRemainingPages;
  5969. ULONG NumPages;
  5970. LONG PreviousPageIdx;
  5971. ULONG FileNameSize;
  5972. BOOLEAN ScenarioVerified;
  5973. PCHAR MetadataInfoBase;
  5974. PPF_METADATA_RECORD MetadataRecordTable;
  5975. PPF_METADATA_RECORD MetadataRecord;
  5976. ULONG MetadataRecordIdx;
  5977. PWCHAR VolumePath;
  5978. PFILE_PREFETCH FilePrefetchInfo;
  5979. ULONG FilePrefetchInfoSize;
  5980. PPF_COUNTED_STRING DirectoryPath;
  5981. ULONG DirectoryIdx;
  5982. //
  5983. // Initialize locals.
  5984. //
  5985. FailedCheckId = 0;
  5986. //
  5987. // Initialize return value to FALSE. It will be set to TRUE only
  5988. // after all the checks pass.
  5989. //
  5990. ScenarioVerified = FALSE;
  5991. //
  5992. // The buffer should at least contain the scenario header.
  5993. //
  5994. if (BufferSize < sizeof(PF_SCENARIO_HEADER)) {
  5995. FailedCheckId = 10;
  5996. goto cleanup;
  5997. }
  5998. //
  5999. // Check version and magic on the header.
  6000. //
  6001. if (Scenario->Version != PF_CURRENT_VERSION ||
  6002. Scenario->MagicNumber != PF_SCENARIO_MAGIC_NUMBER) {
  6003. FailedCheckId = 20;
  6004. goto cleanup;
  6005. }
  6006. //
  6007. // The buffer should not be greater than max allowed size.
  6008. //
  6009. if (BufferSize > PF_MAXIMUM_SCENARIO_SIZE) {
  6010. FailedCheckId = 25;
  6011. goto cleanup;
  6012. }
  6013. //
  6014. // Check for legal scenario type.
  6015. //
  6016. if (Scenario->ScenarioType >= PfMaxScenarioType) {
  6017. FailedCheckId = 27;
  6018. goto cleanup;
  6019. }
  6020. //
  6021. // Check limits on number of pages, sections etc.
  6022. //
  6023. if (Scenario->NumSections > PF_MAXIMUM_SECTIONS ||
  6024. Scenario->NumMetadataRecords > PF_MAXIMUM_SECTIONS ||
  6025. Scenario->NumPages > PF_MAXIMUM_PAGES ||
  6026. Scenario->FileNameInfoSize > PF_MAXIMUM_FILE_NAME_DATA_SIZE) {
  6027. FailedCheckId = 30;
  6028. goto cleanup;
  6029. }
  6030. if (Scenario->NumSections == 0 ||
  6031. Scenario->NumPages == 0 ||
  6032. Scenario->FileNameInfoSize == 0) {
  6033. FailedCheckId = 33;
  6034. goto cleanup;
  6035. }
  6036. //
  6037. // Check limit on sensitivity.
  6038. //
  6039. if (Scenario->Sensitivity < PF_MIN_SENSITIVITY ||
  6040. Scenario->Sensitivity > PF_MAX_SENSITIVITY) {
  6041. FailedCheckId = 35;
  6042. goto cleanup;
  6043. }
  6044. //
  6045. // Make sure the scenario id is valid.
  6046. //
  6047. if (!PfVerifyScenarioId(&Scenario->ScenarioId)) {
  6048. FailedCheckId = 37;
  6049. goto cleanup;
  6050. }
  6051. //
  6052. // Initialize pointers to tables.
  6053. //
  6054. Sections = (PPF_SECTION_RECORD) ((PCHAR)Scenario + Scenario->SectionInfoOffset);
  6055. if (!PfWithinBounds(Sections, Scenario, BufferSize)) {
  6056. FailedCheckId = 40;
  6057. goto cleanup;
  6058. }
  6059. if (!PfWithinBounds((PCHAR) &Sections[Scenario->NumSections] - 1,
  6060. Scenario,
  6061. BufferSize)) {
  6062. FailedCheckId = 45;
  6063. goto cleanup;
  6064. }
  6065. Pages = (PPF_PAGE_RECORD) ((PCHAR)Scenario + Scenario->PageInfoOffset);
  6066. if (!PfWithinBounds(Pages, Scenario, BufferSize)) {
  6067. FailedCheckId = 50;
  6068. goto cleanup;
  6069. }
  6070. if (!PfWithinBounds((PCHAR) &Pages[Scenario->NumPages] - 1,
  6071. Scenario,
  6072. BufferSize)) {
  6073. FailedCheckId = 55;
  6074. goto cleanup;
  6075. }
  6076. FileNames = (PCHAR)Scenario + Scenario->FileNameInfoOffset;
  6077. if (!PfWithinBounds(FileNames, Scenario, BufferSize)) {
  6078. FailedCheckId = 60;
  6079. goto cleanup;
  6080. }
  6081. if (!PfWithinBounds(FileNames + Scenario->FileNameInfoSize - 1,
  6082. Scenario,
  6083. BufferSize)) {
  6084. FailedCheckId = 70;
  6085. goto cleanup;
  6086. }
  6087. MetadataInfoBase = (PCHAR)Scenario + Scenario->MetadataInfoOffset;
  6088. MetadataRecordTable = (PPF_METADATA_RECORD) MetadataInfoBase;
  6089. if (!PfWithinBounds(MetadataInfoBase, Scenario, BufferSize)) {
  6090. FailedCheckId = 73;
  6091. goto cleanup;
  6092. }
  6093. if (!PfWithinBounds(MetadataInfoBase + Scenario->MetadataInfoSize - 1,
  6094. Scenario,
  6095. BufferSize)) {
  6096. FailedCheckId = 74;
  6097. goto cleanup;
  6098. }
  6099. if (!PfWithinBounds(((PCHAR) &MetadataRecordTable[Scenario->NumMetadataRecords]) - 1,
  6100. Scenario,
  6101. BufferSize)) {
  6102. FailedCheckId = 75;
  6103. goto cleanup;
  6104. }
  6105. //
  6106. // Verify that sections contain valid information.
  6107. //
  6108. NumRemainingPages = Scenario->NumPages;
  6109. for (SectionIdx = 0; SectionIdx < Scenario->NumSections; SectionIdx++) {
  6110. pSection = &Sections[SectionIdx];
  6111. //
  6112. // Check if file name is within bounds.
  6113. //
  6114. pFileNameStart = FileNames + pSection->FileNameOffset;
  6115. if (!PfWithinBounds(pFileNameStart, Scenario, BufferSize)) {
  6116. FailedCheckId = 80;
  6117. goto cleanup;
  6118. }
  6119. //
  6120. // Make sure there is a valid sized file name.
  6121. //
  6122. if (pSection->FileNameLength == 0) {
  6123. FailedCheckId = 90;
  6124. goto cleanup;
  6125. }
  6126. //
  6127. // Check file name max length.
  6128. //
  6129. if (pSection->FileNameLength > PF_MAXIMUM_SECTION_FILE_NAME_LENGTH) {
  6130. FailedCheckId = 100;
  6131. goto cleanup;
  6132. }
  6133. //
  6134. // Note that pFileNameEnd gets a -1 so it is the address of
  6135. // the last byte.
  6136. //
  6137. FileNameSize = (pSection->FileNameLength + 1) * sizeof(WCHAR);
  6138. pFileNameEnd = pFileNameStart + FileNameSize - 1;
  6139. if (!PfWithinBounds(pFileNameEnd, Scenario, BufferSize)) {
  6140. FailedCheckId = 110;
  6141. goto cleanup;
  6142. }
  6143. //
  6144. // Check if the file name is NUL terminated.
  6145. //
  6146. pwFileName = (PWCHAR) pFileNameStart;
  6147. if (pwFileName[pSection->FileNameLength] != 0) {
  6148. FailedCheckId = 120;
  6149. goto cleanup;
  6150. }
  6151. //
  6152. // Check max number of pages in a section.
  6153. //
  6154. if (pSection->NumPages > PF_MAXIMUM_SECTION_PAGES) {
  6155. FailedCheckId = 140;
  6156. goto cleanup;
  6157. }
  6158. //
  6159. // Make sure NumPages for the section is at least less
  6160. // than the remaining pages in the scenario. Then update the
  6161. // remaining pages.
  6162. //
  6163. if (pSection->NumPages > NumRemainingPages) {
  6164. FailedCheckId = 150;
  6165. goto cleanup;
  6166. }
  6167. NumRemainingPages -= pSection->NumPages;
  6168. //
  6169. // Verify that there are NumPages pages in our page list and
  6170. // they are sorted by file offset.
  6171. //
  6172. PageIdx = pSection->FirstPageIdx;
  6173. NumPages = 0;
  6174. PreviousPageIdx = PF_INVALID_PAGE_IDX;
  6175. while (PageIdx != PF_INVALID_PAGE_IDX) {
  6176. //
  6177. // Check that page idx is within range.
  6178. //
  6179. if (PageIdx < 0 || (ULONG) PageIdx >= Scenario->NumPages) {
  6180. FailedCheckId = 160;
  6181. goto cleanup;
  6182. }
  6183. //
  6184. // If this is not the first page record, make sure it
  6185. // comes after the previous one. We also check for
  6186. // duplicate offset here.
  6187. //
  6188. if (PreviousPageIdx != PF_INVALID_PAGE_IDX) {
  6189. if (Pages[PageIdx].FileOffset <=
  6190. Pages[PreviousPageIdx].FileOffset) {
  6191. FailedCheckId = 165;
  6192. goto cleanup;
  6193. }
  6194. }
  6195. //
  6196. // Update the last page index.
  6197. //
  6198. PreviousPageIdx = PageIdx;
  6199. //
  6200. // Get the next page index.
  6201. //
  6202. pPage = &Pages[PageIdx];
  6203. PageIdx = pPage->NextPageIdx;
  6204. //
  6205. // Update the number of pages we've seen on the list so
  6206. // far. If it is greater than what there should be on the
  6207. // list we have a problem. We may have even hit a list.
  6208. //
  6209. NumPages++;
  6210. if (NumPages > pSection->NumPages) {
  6211. FailedCheckId = 170;
  6212. goto cleanup;
  6213. }
  6214. }
  6215. //
  6216. // Make sure the section has exactly the number of pages it
  6217. // says it does.
  6218. //
  6219. if (NumPages != pSection->NumPages) {
  6220. FailedCheckId = 180;
  6221. goto cleanup;
  6222. }
  6223. }
  6224. //
  6225. // We should have accounted for all pages in the scenario.
  6226. //
  6227. if (NumRemainingPages) {
  6228. FailedCheckId = 190;
  6229. goto cleanup;
  6230. }
  6231. //
  6232. // Make sure metadata prefetch records make sense.
  6233. //
  6234. for (MetadataRecordIdx = 0;
  6235. MetadataRecordIdx < Scenario->NumMetadataRecords;
  6236. MetadataRecordIdx++) {
  6237. MetadataRecord = &MetadataRecordTable[MetadataRecordIdx];
  6238. //
  6239. // Make sure that the volume path is within bounds and NUL
  6240. // terminated.
  6241. //
  6242. VolumePath = (PWCHAR)(MetadataInfoBase + MetadataRecord->VolumeNameOffset);
  6243. if (!PfWithinBounds(VolumePath, Scenario, BufferSize)) {
  6244. FailedCheckId = 200;
  6245. goto cleanup;
  6246. }
  6247. if (!PfWithinBounds(((PCHAR)(VolumePath + MetadataRecord->VolumeNameLength + 1)) - 1,
  6248. Scenario,
  6249. BufferSize)) {
  6250. FailedCheckId = 210;
  6251. goto cleanup;
  6252. }
  6253. if (VolumePath[MetadataRecord->VolumeNameLength] != 0) {
  6254. FailedCheckId = 220;
  6255. goto cleanup;
  6256. }
  6257. //
  6258. // Make sure that FilePrefetchInformation is within bounds.
  6259. //
  6260. FilePrefetchInfo = (PFILE_PREFETCH)
  6261. (MetadataInfoBase + MetadataRecord->FilePrefetchInfoOffset);
  6262. if (!PfWithinBounds(FilePrefetchInfo, Scenario, BufferSize)) {
  6263. FailedCheckId = 230;
  6264. goto cleanup;
  6265. }
  6266. //
  6267. // Its size should be greater than size of a FILE_PREFETCH
  6268. // structure (so we can safely access the fields).
  6269. //
  6270. if (MetadataRecord->FilePrefetchInfoSize < sizeof(FILE_PREFETCH)) {
  6271. FailedCheckId = 240;
  6272. goto cleanup;
  6273. }
  6274. //
  6275. // It should be for prefetching file creates.
  6276. //
  6277. if (FilePrefetchInfo->Type != FILE_PREFETCH_TYPE_FOR_CREATE) {
  6278. FailedCheckId = 250;
  6279. goto cleanup;
  6280. }
  6281. //
  6282. // There should not be more entries then are files and
  6283. // directories. The number of inidividual directories may be
  6284. // more than what we allow for, but it would be highly rare to
  6285. // be suspicious and thus ignored.
  6286. //
  6287. if (FilePrefetchInfo->Count > PF_MAXIMUM_DIRECTORIES + PF_MAXIMUM_SECTIONS) {
  6288. FailedCheckId = 260;
  6289. goto cleanup;
  6290. }
  6291. //
  6292. // Its size should match the size calculated by number of file
  6293. // index numbers specified in the header.
  6294. //
  6295. FilePrefetchInfoSize = sizeof(FILE_PREFETCH);
  6296. if (FilePrefetchInfo->Count) {
  6297. FilePrefetchInfoSize += (FilePrefetchInfo->Count - 1) * sizeof(ULONGLONG);
  6298. }
  6299. if (!PfWithinBounds((PCHAR) FilePrefetchInfo + MetadataRecord->FilePrefetchInfoSize - 1,
  6300. Scenario,
  6301. BufferSize)) {
  6302. FailedCheckId = 270;
  6303. goto cleanup;
  6304. }
  6305. //
  6306. // Make sure that the directory paths for this volume make
  6307. // sense.
  6308. //
  6309. if (MetadataRecord->NumDirectories > PF_MAXIMUM_DIRECTORIES) {
  6310. FailedCheckId = 280;
  6311. goto cleanup;
  6312. }
  6313. DirectoryPath = (PPF_COUNTED_STRING)
  6314. (MetadataInfoBase + MetadataRecord->DirectoryPathsOffset);
  6315. for (DirectoryIdx = 0;
  6316. DirectoryIdx < MetadataRecord->NumDirectories;
  6317. DirectoryIdx ++) {
  6318. //
  6319. // Make sure head of the structure is within bounds.
  6320. //
  6321. if (!PfWithinBounds((PCHAR)DirectoryPath + sizeof(PF_COUNTED_STRING) - 1,
  6322. Scenario,
  6323. BufferSize)) {
  6324. FailedCheckId = 290;
  6325. goto cleanup;
  6326. }
  6327. //
  6328. // Check the length of the string.
  6329. //
  6330. if (DirectoryPath->Length >= PF_MAXIMUM_SECTION_FILE_NAME_LENGTH) {
  6331. FailedCheckId = 300;
  6332. goto cleanup;
  6333. }
  6334. //
  6335. // Make sure end of the string is within bounds.
  6336. //
  6337. if (!PfWithinBounds((PCHAR)(&DirectoryPath->String[DirectoryPath->Length + 1]) - 1,
  6338. Scenario,
  6339. BufferSize)) {
  6340. FailedCheckId = 310;
  6341. goto cleanup;
  6342. }
  6343. //
  6344. // Make sure the string is NUL terminated.
  6345. //
  6346. if (DirectoryPath->String[DirectoryPath->Length] != 0) {
  6347. FailedCheckId = 320;
  6348. goto cleanup;
  6349. }
  6350. //
  6351. // Set pointer to next DirectoryPath.
  6352. //
  6353. DirectoryPath = (PPF_COUNTED_STRING)
  6354. (&DirectoryPath->String[DirectoryPath->Length + 1]);
  6355. }
  6356. }
  6357. //
  6358. // We've passed all the checks.
  6359. //
  6360. ScenarioVerified = TRUE;
  6361. cleanup:
  6362. *FailedCheck = FailedCheckId;
  6363. return ScenarioVerified;
  6364. }
  6365. BOOLEAN
  6366. PfVerifyTraceBuffer(
  6367. PPF_TRACE_HEADER Trace,
  6368. ULONG BufferSize,
  6369. PULONG FailedCheck
  6370. )
  6371. /*++
  6372. Routine Description:
  6373. Verify offset and indices in a trace buffer are not beyond
  6374. bounds. This code is shared between the user mode service and
  6375. kernel mode component. If you update this function, update it in
  6376. both.
  6377. Arguments:
  6378. Trace - Base of Trace buffer.
  6379. BufferSize - Size of the scenario file / mapping.
  6380. FailedCheck - If verify failed, Id for the check that was failed.
  6381. Return Value:
  6382. TRUE - Trace is fine.
  6383. FALSE - Trace is corrupt;
  6384. --*/
  6385. {
  6386. LONG FailedCheckId;
  6387. PPF_LOG_ENTRY LogEntries;
  6388. PPF_SECTION_INFO Section;
  6389. PPF_VOLUME_INFO VolumeInfo;
  6390. ULONG SectionLength;
  6391. ULONG EntryIdx;
  6392. ULONG SectionIdx;
  6393. ULONG TotalFaults;
  6394. ULONG PeriodIdx;
  6395. ULONG VolumeIdx;
  6396. BOOLEAN TraceVerified;
  6397. ULONG VolumeInfoSize;
  6398. //
  6399. // Initialize locals:
  6400. //
  6401. FailedCheckId = 0;
  6402. //
  6403. // Initialize return value to FALSE. It will be set to TRUE only
  6404. // after all the checks pass.
  6405. //
  6406. TraceVerified = FALSE;;
  6407. //
  6408. // The buffer should at least contain the scenario header.
  6409. //
  6410. if (BufferSize < sizeof(PF_TRACE_HEADER)) {
  6411. FailedCheckId = 10;
  6412. goto cleanup;
  6413. }
  6414. //
  6415. // Check version and magic on the header.
  6416. //
  6417. if (Trace->Version != PF_CURRENT_VERSION ||
  6418. Trace->MagicNumber != PF_TRACE_MAGIC_NUMBER) {
  6419. FailedCheckId = 20;
  6420. goto cleanup;
  6421. }
  6422. //
  6423. // The buffer should not be greater than max allowed size.
  6424. //
  6425. if (BufferSize > PF_MAXIMUM_TRACE_SIZE) {
  6426. FailedCheckId = 23;
  6427. goto cleanup;
  6428. }
  6429. //
  6430. // Check for legal scenario type.
  6431. //
  6432. if (Trace->ScenarioType >= PfMaxScenarioType) {
  6433. FailedCheckId = 25;
  6434. goto cleanup;
  6435. }
  6436. //
  6437. // Check limits on number of pages, sections etc.
  6438. //
  6439. if (Trace->NumSections > PF_MAXIMUM_SECTIONS ||
  6440. Trace->NumEntries > PF_MAXIMUM_LOG_ENTRIES ||
  6441. Trace->NumVolumes > PF_MAXIMUM_SECTIONS) {
  6442. FailedCheckId = 30;
  6443. goto cleanup;
  6444. }
  6445. //
  6446. // Check buffer size and the size of the trace.
  6447. //
  6448. if (Trace->Size != BufferSize) {
  6449. FailedCheckId = 35;
  6450. goto cleanup;
  6451. }
  6452. //
  6453. // Make sure the scenario id is valid.
  6454. //
  6455. if (!PfVerifyScenarioId(&Trace->ScenarioId)) {
  6456. FailedCheckId = 37;
  6457. goto cleanup;
  6458. }
  6459. //
  6460. // Check Bounds of Trace Buffer
  6461. //
  6462. LogEntries = (PPF_LOG_ENTRY) ((PCHAR)Trace + Trace->TraceBufferOffset);
  6463. if (!PfWithinBounds(LogEntries, Trace, BufferSize)) {
  6464. FailedCheckId = 40;
  6465. goto cleanup;
  6466. }
  6467. if (!PfWithinBounds((PCHAR)&LogEntries[Trace->NumEntries] - 1,
  6468. Trace,
  6469. BufferSize)) {
  6470. FailedCheckId = 50;
  6471. goto cleanup;
  6472. }
  6473. //
  6474. // Verify pages contain valid information.
  6475. //
  6476. for (EntryIdx = 0; EntryIdx < Trace->NumEntries; EntryIdx++) {
  6477. //
  6478. // Make sure sequence number is within bounds.
  6479. //
  6480. if (LogEntries[EntryIdx].SectionId >= Trace->NumSections) {
  6481. FailedCheckId = 60;
  6482. goto cleanup;
  6483. }
  6484. }
  6485. //
  6486. // Verify section info entries are valid.
  6487. //
  6488. Section = (PPF_SECTION_INFO) ((PCHAR)Trace + Trace->SectionInfoOffset);
  6489. for (SectionIdx = 0; SectionIdx < Trace->NumSections; SectionIdx++) {
  6490. //
  6491. // Make sure the section is within bounds.
  6492. //
  6493. if (!PfWithinBounds(Section, Trace, BufferSize)) {
  6494. FailedCheckId = 70;
  6495. goto cleanup;
  6496. }
  6497. //
  6498. // Make sure the file name is not too big.
  6499. //
  6500. if(Section->FileNameLength > PF_MAXIMUM_SECTION_FILE_NAME_LENGTH) {
  6501. FailedCheckId = 80;
  6502. goto cleanup;
  6503. }
  6504. //
  6505. // Make sure the file name is NUL terminated.
  6506. //
  6507. if (Section->FileName[Section->FileNameLength] != 0) {
  6508. FailedCheckId = 90;
  6509. goto cleanup;
  6510. }
  6511. //
  6512. // Calculate size of this section entry.
  6513. //
  6514. SectionLength = sizeof(PF_SECTION_INFO) +
  6515. (Section->FileNameLength) * sizeof(WCHAR);
  6516. //
  6517. // Make sure all of the data in the section info is within
  6518. // bounds.
  6519. //
  6520. if (!PfWithinBounds((PUCHAR)Section + SectionLength - 1,
  6521. Trace,
  6522. BufferSize)) {
  6523. FailedCheckId = 100;
  6524. goto cleanup;
  6525. }
  6526. //
  6527. // Set pointer to next section.
  6528. //
  6529. Section = (PPF_SECTION_INFO) ((PUCHAR) Section + SectionLength);
  6530. }
  6531. //
  6532. // Check FaultsPerPeriod information.
  6533. //
  6534. if (!PfWithinBounds((PCHAR)&Trace->FaultsPerPeriod[PF_MAX_NUM_TRACE_PERIODS] - 1,
  6535. Trace,
  6536. BufferSize)) {
  6537. FailedCheckId = 110;
  6538. goto cleanup;
  6539. }
  6540. TotalFaults = 0;
  6541. for (PeriodIdx = 0; PeriodIdx < PF_MAX_NUM_TRACE_PERIODS; PeriodIdx++) {
  6542. TotalFaults += Trace->FaultsPerPeriod[PeriodIdx];
  6543. }
  6544. if (TotalFaults > Trace->NumEntries) {
  6545. FailedCheckId = 120;
  6546. goto cleanup;
  6547. }
  6548. //
  6549. // Verify the volume information block.
  6550. //
  6551. VolumeInfo = (PPF_VOLUME_INFO) ((PCHAR)Trace + Trace->VolumeInfoOffset);
  6552. if (!PfWithinBounds(VolumeInfo, Trace, BufferSize)) {
  6553. FailedCheckId = 130;
  6554. goto cleanup;
  6555. }
  6556. if (!PfWithinBounds((PCHAR)VolumeInfo + Trace->VolumeInfoSize - 1,
  6557. Trace,
  6558. BufferSize)) {
  6559. FailedCheckId = 140;
  6560. goto cleanup;
  6561. }
  6562. //
  6563. // If there are sections, we should have at least one volume.
  6564. //
  6565. if (Trace->NumSections && !Trace->NumVolumes) {
  6566. FailedCheckId = 150;
  6567. goto cleanup;
  6568. }
  6569. //
  6570. // Verify the volume info structures per volume.
  6571. //
  6572. for (VolumeIdx = 0; VolumeIdx < Trace->NumVolumes; VolumeIdx++) {
  6573. //
  6574. // Make sure the whole volume structure is within bounds. Note
  6575. // that VolumeInfo structure contains space for the
  6576. // terminating NUL.
  6577. //
  6578. VolumeInfoSize = sizeof(PF_VOLUME_INFO);
  6579. VolumeInfoSize += VolumeInfo->VolumePathLength * sizeof(WCHAR);
  6580. if (!PfWithinBounds((PCHAR) VolumeInfo + VolumeInfoSize - 1,
  6581. Trace,
  6582. BufferSize)) {
  6583. FailedCheckId = 160;
  6584. goto cleanup;
  6585. }
  6586. //
  6587. // Verify that the volume path string is terminated.
  6588. //
  6589. if (VolumeInfo->VolumePath[VolumeInfo->VolumePathLength] != 0) {
  6590. FailedCheckId = 170;
  6591. goto cleanup;
  6592. }
  6593. //
  6594. // Get the next volume.
  6595. //
  6596. VolumeInfo = (PPF_VOLUME_INFO) ((PCHAR) VolumeInfo + VolumeInfoSize);
  6597. //
  6598. // Make sure VolumeInfo is aligned.
  6599. //
  6600. VolumeInfo = PF_ALIGN_UP(VolumeInfo, _alignof(PF_VOLUME_INFO));
  6601. }
  6602. //
  6603. // We've passed all the checks.
  6604. //
  6605. TraceVerified = TRUE;
  6606. cleanup:
  6607. *FailedCheck = FailedCheckId;
  6608. return TraceVerified;
  6609. }