Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

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