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.

17905 lines
438 KiB

  1. /*++
  2. Copyright (c) 2000 Microsoft Corporation
  3. Module Name:
  4. pfsvc.c
  5. Abstract:
  6. This module contains the main rountines for the prefetcher service
  7. responsible for maintaining prefetch scenario files.
  8. Author:
  9. Stuart Sechrest (stuartse)
  10. Cenk Ergan (cenke)
  11. Chuck Leinzmeier (chuckl)
  12. Environment:
  13. User Mode
  14. --*/
  15. #include <nt.h>
  16. #include <ntrtl.h>
  17. #include <nturtl.h>
  18. #include <windows.h>
  19. #include <stdio.h>
  20. #include <stdlib.h>
  21. #include <limits.h>
  22. #include <aclapi.h>
  23. #include <dbghelp.h>
  24. #include <idletask.h>
  25. #include <prefetch.h>
  26. #include <shdcom.h>
  27. #include <tchar.h>
  28. #include "pfsvc.h"
  29. //
  30. // Routine called to register for notifications when processing of all idle
  31. // tasks is requested from the idle task server.
  32. //
  33. typedef VOID (*PIT_PROCESS_IDLE_TASKS_NOTIFY_ROUTINE)(VOID);
  34. BOOL
  35. ItSpSetProcessIdleTasksNotifyRoutine (
  36. PIT_PROCESS_IDLE_TASKS_NOTIFY_ROUTINE NotifyRoutine
  37. );
  38. //
  39. // Globals.
  40. //
  41. PFSVC_GLOBALS PfSvcGlobals = {0};
  42. //
  43. // Exposed routines:
  44. //
  45. DWORD
  46. WINAPI
  47. PfSvcMainThread(
  48. VOID *Param
  49. )
  50. /*++
  51. Routine Description:
  52. This is the main routine for the prefetcher service. It sets up
  53. file notification on the input files directory and waits for work
  54. or the signaling of the termination event.
  55. Arguments:
  56. Param - Pointer to handle to the event that will signal our
  57. termination.
  58. Return Value:
  59. Win32 error code.
  60. --*/
  61. {
  62. HANDLE hStopEvent;
  63. HANDLE hTracesReadyEvent;
  64. HANDLE hParametersChangedEvent;
  65. HANDLE hEvents[4];
  66. ULONG NumEvents;
  67. DWORD ErrorCode;
  68. BOOLEAN bExitMainLoop;
  69. DWORD dwWait;
  70. NTSTATUS Status;
  71. PF_SCENARIO_TYPE ScenarioType;
  72. BOOLEAN UpdatedParameters;
  73. BOOLEAN PrefetchingEnabled;
  74. HANDLE PrefetcherThreads[1];
  75. ULONG NumPrefetcherThreads;
  76. ULONG ThreadIdx;
  77. //
  78. // Initialize locals.
  79. //
  80. // FUTURE-2002/03/29-ScottMa -- NumEvents should be set where the events
  81. // array is initialized, and asserted against the maximum value.
  82. NumEvents = sizeof(hEvents) / sizeof(HANDLE);
  83. hStopEvent = *((HANDLE *) Param);
  84. hTracesReadyEvent = NULL;
  85. hParametersChangedEvent = NULL;
  86. NumPrefetcherThreads = 0;
  87. DBGPR((PFID,PFTRC,"PFSVC: MainThread()\n"));
  88. //
  89. // Initialize globals.
  90. //
  91. ErrorCode = PfSvInitializeGlobals();
  92. if (ErrorCode != ERROR_SUCCESS) {
  93. DBGPR((PFID,PFERR,"PFSVC: MainThread()-FailedInitGlobals\n"));
  94. goto cleanup;
  95. }
  96. //
  97. // Save service start time, prefetcher version etc.
  98. //
  99. PfSvSaveStartInfo(PfSvcGlobals.ServiceDataKey);
  100. //
  101. // Get necessary permissions for this thread to perform prefetch
  102. // service tasks.
  103. //
  104. ErrorCode = PfSvGetPrefetchServiceThreadPrivileges();
  105. if (ErrorCode != ERROR_SUCCESS) {
  106. DBGPR((PFID,PFERR,"PFSVC: MainThread()-FailedGetPrivileges\n"));
  107. goto cleanup;
  108. }
  109. //
  110. // Set permissions on the event that can be set to override
  111. // waiting for system to be idle before processing traces, so it
  112. // can be set by administrators.
  113. //
  114. ErrorCode = PfSvSetAdminOnlyPermissions(PFSVC_OVERRIDE_IDLE_EVENT_NAME,
  115. PfSvcGlobals.OverrideIdleProcessingEvent,
  116. SE_KERNEL_OBJECT);
  117. if (ErrorCode != ERROR_SUCCESS) {
  118. DBGPR((PFID,PFERR,"PFSVC: MainThread()-FailedSetPermissions1\n"));
  119. goto cleanup;
  120. }
  121. ErrorCode = PfSvSetAdminOnlyPermissions(PFSVC_PROCESSING_COMPLETE_EVENT_NAME,
  122. PfSvcGlobals.ProcessingCompleteEvent,
  123. SE_KERNEL_OBJECT);
  124. if (ErrorCode != ERROR_SUCCESS) {
  125. DBGPR((PFID,PFERR,"PFSVC: MainThread()-FailedSetPermissions2\n"));
  126. goto cleanup;
  127. }
  128. //
  129. // Get system prefetch parameters.
  130. //
  131. ErrorCode = PfSvQueryPrefetchParameters(&PfSvcGlobals.Parameters);
  132. if (ErrorCode != ERROR_SUCCESS) {
  133. DBGPR((PFID,PFERR,"PFSVC: MainThread()-FailedQueryParameters\n"));
  134. goto cleanup;
  135. }
  136. //
  137. // Depending on system type, if various types of prefetching is
  138. // not specified in the registry (i.e. not specifically disabled),
  139. // enable it.
  140. //
  141. UpdatedParameters = FALSE;
  142. if (PfSvcGlobals.OsVersion.wProductType == VER_NT_WORKSTATION) {
  143. //
  144. // Enable all prefetching types if they are not disabled.
  145. //
  146. for(ScenarioType = 0; ScenarioType < PfMaxScenarioType; ScenarioType++) {
  147. if (PfSvcGlobals.Parameters.EnableStatus[ScenarioType] == PfSvNotSpecified) {
  148. PfSvcGlobals.Parameters.EnableStatus[ScenarioType] = PfSvEnabled;
  149. UpdatedParameters = TRUE;
  150. }
  151. }
  152. } else if (PfSvcGlobals.OsVersion.wProductType == VER_NT_SERVER ||
  153. PfSvcGlobals.OsVersion.wProductType == VER_NT_DOMAIN_CONTROLLER) {
  154. //
  155. // Enable only boot prefetching.
  156. //
  157. if (PfSvcGlobals.Parameters.EnableStatus[PfSystemBootScenarioType] == PfSvNotSpecified) {
  158. PfSvcGlobals.Parameters.EnableStatus[PfSystemBootScenarioType] = PfSvEnabled;
  159. UpdatedParameters = TRUE;
  160. }
  161. }
  162. //
  163. // If we enabled prefetching for a scenario type, call the kernel
  164. // to update the parameters.
  165. //
  166. if (UpdatedParameters) {
  167. ErrorCode = PfSvSetPrefetchParameters(&PfSvcGlobals.Parameters);
  168. if (ErrorCode != ERROR_SUCCESS) {
  169. DBGPR((PFID,PFERR,"PFSVC: MainThread()-FailedSetParameters\n"));
  170. goto cleanup;
  171. }
  172. }
  173. //
  174. // Continue only if prefetching for a scenario type is enabled.
  175. //
  176. PrefetchingEnabled = FALSE;
  177. for(ScenarioType = 0; ScenarioType < PfMaxScenarioType; ScenarioType++) {
  178. if (PfSvcGlobals.Parameters.EnableStatus[ScenarioType] == PfSvEnabled) {
  179. PrefetchingEnabled = TRUE;
  180. break;
  181. }
  182. }
  183. if (PrefetchingEnabled == FALSE) {
  184. ErrorCode = ERROR_NOT_SUPPORTED;
  185. DBGPR((PFID,PFERR,"PFSVC: MainThread()-PrefetchingNotEnabled\n"));
  186. goto cleanup;
  187. }
  188. //
  189. // Initialize the directory that contains prefetch instructions.
  190. //
  191. ErrorCode = PfSvInitializePrefetchDirectory(PfSvcGlobals.Parameters.RootDirPath);
  192. if (ErrorCode != ERROR_SUCCESS) {
  193. DBGPR((PFID,PFERR,"PFSVC: MainThread()-FailedInitPrefetchDir\n"));
  194. goto cleanup;
  195. }
  196. //
  197. // Create the event that the kernel will set when raw traces are
  198. // available. Then set the event so that the first time into the loop we
  199. // will immediately process whatever raw traces are already waiting.
  200. //
  201. // The event is an autoclearing event, so it resets to the not-signaled
  202. // state when our wait is satisfied. This allows proper synchronization
  203. // with the kernel prefetcher.
  204. //
  205. hTracesReadyEvent = CreateEvent(NULL,
  206. FALSE,
  207. FALSE,
  208. PF_COMPLETED_TRACES_EVENT_WIN32_NAME);
  209. if (hTracesReadyEvent == NULL) {
  210. ErrorCode = GetLastError();
  211. DBGPR((PFID,PFERR,"PFSVC: MainThread()-FailedTracesReadyEvent\n"));
  212. goto cleanup;
  213. }
  214. SetEvent(hTracesReadyEvent);
  215. //
  216. // Create the event that the kernel will set when system prefetch
  217. // parameters change.
  218. //
  219. hParametersChangedEvent = CreateEvent(NULL,
  220. FALSE,
  221. FALSE,
  222. PF_PARAMETERS_CHANGED_EVENT_WIN32_NAME);
  223. if (hParametersChangedEvent == NULL) {
  224. ErrorCode = GetLastError();
  225. DBGPR((PFID,PFERR,"PFSVC: MainThread()-FailedParamsChangedEvent\n"));
  226. goto cleanup;
  227. }
  228. //
  229. // Set permissions on the events kernel uses to communicate with us.
  230. //
  231. ErrorCode = PfSvSetAdminOnlyPermissions(PF_COMPLETED_TRACES_EVENT_WIN32_NAME,
  232. hTracesReadyEvent,
  233. SE_KERNEL_OBJECT);
  234. if (ErrorCode != ERROR_SUCCESS) {
  235. DBGPR((PFID,PFERR,"PFSVC: MainThread()-FailedSetPermissions3\n"));
  236. goto cleanup;
  237. }
  238. ErrorCode = PfSvSetAdminOnlyPermissions(PF_PARAMETERS_CHANGED_EVENT_WIN32_NAME,
  239. hParametersChangedEvent,
  240. SE_KERNEL_OBJECT);
  241. if (ErrorCode != ERROR_SUCCESS) {
  242. DBGPR((PFID,PFERR,"PFSVC: MainThread()-FailedSetPermissions3\n"));
  243. goto cleanup;
  244. }
  245. //
  246. // Queue a work item to wait for the shell ready event and notify
  247. // the kernel.
  248. //
  249. QueueUserWorkItem(PfSvPollShellReadyWorker, NULL, WT_EXECUTELONGFUNCTION);
  250. //
  251. // Create a thread to process traces retrieved from the kernel.
  252. //
  253. PrefetcherThreads[NumPrefetcherThreads] = CreateThread(NULL,
  254. 0,
  255. PfSvProcessTraceThread,
  256. NULL,
  257. 0,
  258. NULL);
  259. if (PrefetcherThreads[NumPrefetcherThreads]) {
  260. NumPrefetcherThreads++;
  261. }
  262. //
  263. // Register a notification routine with the idle task server.
  264. //
  265. ItSpSetProcessIdleTasksNotifyRoutine(PfSvProcessIdleTasksCallback);
  266. //
  267. // Set up handles we are going to wait on.
  268. //
  269. hEvents[0] = hStopEvent;
  270. hEvents[1] = hTracesReadyEvent;
  271. hEvents[2] = hParametersChangedEvent;
  272. hEvents[3] = PfSvcGlobals.CheckForMissedTracesEvent;
  273. //
  274. // This is the main loop. Wait on the events for work or for exit
  275. // signal.
  276. //
  277. bExitMainLoop = FALSE;
  278. do {
  279. DBGPR((PFID,PFWAIT,"PFSVC: MainThread()-WaitForWork\n"));
  280. dwWait = WaitForMultipleObjects(NumEvents, hEvents, FALSE, INFINITE);
  281. DBGPR((PFID,PFWAIT,"PFSVC: MainThread()-EndWaitForWork=%x\n",dwWait));
  282. switch(dwWait) {
  283. case WAIT_OBJECT_0:
  284. //
  285. // Service exit event:
  286. //
  287. //
  288. // Break out, cleanup and exit.
  289. //
  290. ErrorCode = ERROR_SUCCESS;
  291. bExitMainLoop = TRUE;
  292. break;
  293. case WAIT_OBJECT_0 + 3:
  294. //
  295. // The event that is set when we had max number of queued
  296. // traces and we processed one. We should check for traces
  297. // we could not pick up because the queue had maxed.
  298. //
  299. //
  300. // Fall through to retrieve traces from the kernel.
  301. //
  302. case WAIT_OBJECT_0 + 1:
  303. //
  304. // New traces are available event set by the kernel:
  305. //
  306. PfSvGetRawTraces();
  307. break;
  308. case WAIT_OBJECT_0 + 2:
  309. //
  310. // Prefetch parameters changed event:
  311. //
  312. //
  313. // Get new system prefetch parameters.
  314. //
  315. ErrorCode = PfSvQueryPrefetchParameters(&PfSvcGlobals.Parameters);
  316. //
  317. // If we were not successful, we should not continue.
  318. //
  319. if (ErrorCode != ERROR_SUCCESS) {
  320. bExitMainLoop = TRUE;
  321. DBGPR((PFID,PFERR,"PFSVC: MainThread()-FailedQueryParameters2\n"));
  322. break;
  323. }
  324. //
  325. // Update the path to the prefetch instructions directory.
  326. //
  327. ErrorCode = PfSvInitializePrefetchDirectory(PfSvcGlobals.Parameters.RootDirPath);
  328. if (ErrorCode != ERROR_SUCCESS) {
  329. bExitMainLoop = TRUE;
  330. DBGPR((PFID,PFERR,"PFSVC: MainThread()-FailedReinitPrefetchDir\n"));
  331. break;
  332. }
  333. break;
  334. default:
  335. //
  336. // Something gone wrong. Break out, cleanup and exit.
  337. //
  338. DBGPR((PFID,PFERR,"PFSVC: MainThread()-WaitForWorkFailed\n"));
  339. ErrorCode = ERROR_INVALID_HANDLE;
  340. bExitMainLoop = TRUE;
  341. break;
  342. }
  343. } while (!bExitMainLoop);
  344. cleanup:
  345. //
  346. // If we could create this event, make sure it is signaled when the
  347. // service is exiting, so no one gets stuck on it.
  348. //
  349. if (PfSvcGlobals.ProcessingCompleteEvent) {
  350. SetEvent(PfSvcGlobals.ProcessingCompleteEvent);
  351. }
  352. //
  353. // Save exit information.
  354. //
  355. if (PfSvcGlobals.ServiceDataKey) {
  356. PfSvSaveExitInfo(PfSvcGlobals.ServiceDataKey, ErrorCode);
  357. }
  358. //
  359. // Make sure the terminate event is set and wait for all our
  360. // threads to exit.
  361. //
  362. if (NumPrefetcherThreads) {
  363. //
  364. // We could not have created worker threads without having
  365. // initialized the globals successfully.
  366. //
  367. PFSVC_ASSERT(PfSvcGlobals.TerminateServiceEvent);
  368. SetEvent(PfSvcGlobals.TerminateServiceEvent);
  369. for (ThreadIdx = 0; ThreadIdx < NumPrefetcherThreads; ThreadIdx++) {
  370. PFSVC_ASSERT(PrefetcherThreads[ThreadIdx]);
  371. DBGPR((PFID,PFWAIT,"PFSVC: MainThread()-WaitForThreadIdx(%d)\n", ThreadIdx));
  372. WaitForSingleObject(PrefetcherThreads[ThreadIdx], INFINITE);
  373. DBGPR((PFID,PFWAIT,"PFSVC: MainThread()-EndWaitForThreadIdx(%d)\n", ThreadIdx));
  374. CloseHandle(PrefetcherThreads[ThreadIdx]);
  375. }
  376. }
  377. if (hTracesReadyEvent != NULL) {
  378. CloseHandle(hTracesReadyEvent);
  379. }
  380. if (hParametersChangedEvent != NULL) {
  381. CloseHandle(hParametersChangedEvent);
  382. }
  383. //
  384. // Cleanup all globals.
  385. //
  386. PfSvCleanupGlobals();
  387. DBGPR((PFID,PFTRC,"PFSVC: MainThread()=%x\n", ErrorCode));
  388. return ErrorCode;
  389. }
  390. //
  391. // Internal service routines:
  392. //
  393. //
  394. // Thread routines:
  395. //
  396. DWORD
  397. WINAPI
  398. PfSvProcessTraceThread(
  399. VOID *Param
  400. )
  401. /*++
  402. Routine Description:
  403. This is the routine for the thread that processes traces and
  404. updates scenarios.
  405. Arguments:
  406. Param - Ignored.
  407. Return Value:
  408. Win32 error code.
  409. --*/
  410. {
  411. PFSVC_IDLE_TASK LayoutTask;
  412. PFSVC_IDLE_TASK DirectoryCleanupTask;
  413. PPFSVC_TRACE_BUFFER TraceBuffer;
  414. PLIST_ENTRY HeadEntry;
  415. HANDLE CheckForQueuedTracesEvents[3];
  416. HANDLE BootTraceEvents[2];
  417. DWORD ErrorCode;
  418. ULONG NumCheckForQueuedTracesEvents;
  419. ULONG OrgNumQueuedTraces;
  420. ULONG WaitResult;
  421. ULONG NumEvents;
  422. ULONG NumFailedTraces;
  423. BOOLEAN AcquiredTracesLock;
  424. //
  425. // Intialize locals.
  426. //
  427. TraceBuffer = NULL;
  428. AcquiredTracesLock = FALSE;
  429. PfSvInitializeTask(&LayoutTask);
  430. PfSvInitializeTask(&DirectoryCleanupTask);
  431. NumFailedTraces = 0;
  432. //
  433. // These are the events we wait on before picking up traces to
  434. // process.
  435. //
  436. CheckForQueuedTracesEvents[0] = PfSvcGlobals.TerminateServiceEvent;
  437. CheckForQueuedTracesEvents[1] = PfSvcGlobals.NewTracesToProcessEvent;
  438. CheckForQueuedTracesEvents[2] = PfSvcGlobals.OverrideIdleProcessingEvent;
  439. NumCheckForQueuedTracesEvents = sizeof(CheckForQueuedTracesEvents) / sizeof(HANDLE);
  440. DBGPR((PFID,PFTRC,"PFSVC: ProcessTraceThread()\n"));
  441. //
  442. // Get necessary permissions for this thread to perform prefetch
  443. // service tasks.
  444. //
  445. ErrorCode = PfSvGetPrefetchServiceThreadPrivileges();
  446. if (ErrorCode != ERROR_SUCCESS) {
  447. goto cleanup;
  448. }
  449. //
  450. // If we are allowed to run the defragger...
  451. // We'll update the layout file even if the registry setting prevents us
  452. // from launching the defragger.
  453. //
  454. if (PfSvAllowedToRunDefragger(FALSE)) {
  455. //
  456. // Queue an idle task to check & update the optimal disk layout if
  457. // necessary. Ignore failure to do so.
  458. //
  459. ErrorCode = PfSvRegisterTask(&LayoutTask,
  460. ItOptimalDiskLayoutTaskId,
  461. PfSvCommonTaskCallback,
  462. PfSvUpdateOptimalLayout);
  463. }
  464. //
  465. // Loop forever waiting for traces to process and processing them.
  466. //
  467. while(TRUE) {
  468. //
  469. // Grab queued traces lock to check for queued traces.
  470. //
  471. PFSVC_ASSERT(!AcquiredTracesLock);
  472. PFSVC_ACQUIRE_LOCK(PfSvcGlobals.TracesLock);
  473. AcquiredTracesLock = TRUE;
  474. if (!IsListEmpty(&PfSvcGlobals.Traces)) {
  475. //
  476. // Dequeue and process the first entry in the list.
  477. //
  478. HeadEntry = RemoveHeadList(&PfSvcGlobals.Traces);
  479. TraceBuffer = CONTAINING_RECORD(HeadEntry,
  480. PFSVC_TRACE_BUFFER,
  481. TracesLink);
  482. PFSVC_ASSERT(PfSvcGlobals.NumTraces);
  483. OrgNumQueuedTraces = PfSvcGlobals.NumTraces;
  484. PfSvcGlobals.NumTraces--;
  485. //
  486. // Release the lock.
  487. //
  488. PFSVC_RELEASE_LOCK(PfSvcGlobals.TracesLock);
  489. AcquiredTracesLock = FALSE;
  490. //
  491. // If we had maxed the queue, note to check for traces
  492. // that we may have failed to pick up because the queue
  493. // was full.
  494. //
  495. if (OrgNumQueuedTraces == PFSVC_MAX_NUM_QUEUED_TRACES) {
  496. SetEvent(PfSvcGlobals.CheckForMissedTracesEvent);
  497. //
  498. // Let the thread that queries the kernel for traces
  499. // wake up and run.
  500. //
  501. Sleep(0);
  502. }
  503. //
  504. // Clear the event that says we don't have traces to
  505. // process.
  506. //
  507. ResetEvent(PfSvcGlobals.ProcessingCompleteEvent);
  508. //
  509. // If this is a boot trace, wait for a little while for
  510. // boot to be really over before processing it.
  511. //
  512. if (TraceBuffer->Trace.ScenarioType == PfSystemBootScenarioType) {
  513. BootTraceEvents[0] = PfSvcGlobals.TerminateServiceEvent;
  514. BootTraceEvents[1] = PfSvcGlobals.OverrideIdleProcessingEvent;
  515. NumEvents = 2;
  516. PFSVC_ASSERT(NumEvents <= (sizeof(BootTraceEvents) / sizeof(HANDLE)));
  517. WaitResult = WaitForMultipleObjects(NumEvents,
  518. BootTraceEvents,
  519. FALSE,
  520. 45000); // 45 seconds.
  521. if (WaitResult == WAIT_OBJECT_0) {
  522. ErrorCode = ERROR_SUCCESS;
  523. goto cleanup;
  524. }
  525. }
  526. ErrorCode = PfSvProcessTrace(&TraceBuffer->Trace);
  527. //
  528. // Update statistics.
  529. //
  530. PfSvcGlobals.NumTracesProcessed++;
  531. if (ErrorCode != ERROR_SUCCESS) {
  532. PfSvcGlobals.LastTraceFailure = ErrorCode;
  533. } else {
  534. PfSvcGlobals.NumTracesSuccessful++;
  535. }
  536. //
  537. // Free the trace buffer.
  538. //
  539. VirtualFree(TraceBuffer, 0, MEM_RELEASE);
  540. TraceBuffer = NULL;
  541. //
  542. // Did we just create too many scenario files in the prefetch directory?
  543. // Queue an idle task to clean up.
  544. //
  545. if (PfSvcGlobals.NumPrefetchFiles >= PFSVC_MAX_PREFETCH_FILES) {
  546. if (!DirectoryCleanupTask.Registered) {
  547. //
  548. // Make sure we've cleaned up after a possible previous run.
  549. //
  550. PfSvCleanupTask(&DirectoryCleanupTask);
  551. PfSvInitializeTask(&DirectoryCleanupTask);
  552. ErrorCode = PfSvRegisterTask(&DirectoryCleanupTask,
  553. ItPrefetchDirectoryCleanupTaskId,
  554. PfSvCommonTaskCallback,
  555. PfSvCleanupPrefetchDirectory);
  556. }
  557. }
  558. //
  559. // Every so many scenario launches it is good to see if we should update
  560. // disk layout.
  561. //
  562. if (((PfSvcGlobals.NumTracesSuccessful + 1) % 32) == 0) {
  563. //
  564. // Even if we can't launch the defragger because of the
  565. // registry setting, we'll update layout.ini, so if the user
  566. // manually launches the defragger it will optimize layout
  567. // as well.
  568. //
  569. if (PfSvAllowedToRunDefragger(FALSE)) {
  570. if (!LayoutTask.Registered) {
  571. //
  572. // Make sure we've cleaned up after a possible previous run.
  573. //
  574. PfSvCleanupTask(&LayoutTask);
  575. PfSvInitializeTask(&LayoutTask);
  576. ErrorCode = PfSvRegisterTask(&LayoutTask,
  577. ItOptimalDiskLayoutTaskId,
  578. PfSvCommonTaskCallback,
  579. PfSvUpdateOptimalLayout);
  580. }
  581. }
  582. }
  583. } else {
  584. //
  585. // The list is empty. Signal that we are done with all the
  586. // queued traces if we don't have idle tasks to complete.
  587. //
  588. if (!LayoutTask.Registered &&
  589. !DirectoryCleanupTask.Registered) {
  590. SetEvent(PfSvcGlobals.ProcessingCompleteEvent);
  591. }
  592. //
  593. // Release the lock.
  594. //
  595. PFSVC_RELEASE_LOCK(PfSvcGlobals.TracesLock);
  596. AcquiredTracesLock = FALSE;
  597. //
  598. // Update the statistics if there were new failed traces.
  599. //
  600. // FUTURE-2002/03/29-ScottMa -- The statistics are not updated
  601. // unless there were new failed traces, therefore the logged
  602. // values for number of traces processed is stale.
  603. if (NumFailedTraces != (PfSvcGlobals.NumTracesProcessed -
  604. PfSvcGlobals.NumTracesSuccessful)) {
  605. NumFailedTraces = PfSvcGlobals.NumTracesProcessed -
  606. PfSvcGlobals.NumTracesSuccessful;
  607. PfSvSaveTraceProcessingStatistics(PfSvcGlobals.ServiceDataKey);
  608. }
  609. //
  610. // Wait until new traces are queued.
  611. //
  612. DBGPR((PFID,PFWAIT,"PFSVC: ProcessTraceThread()-WaitForTrace\n"));
  613. NumEvents = NumCheckForQueuedTracesEvents;
  614. WaitResult = WaitForMultipleObjects(NumEvents,
  615. CheckForQueuedTracesEvents,
  616. FALSE,
  617. INFINITE);
  618. DBGPR((PFID,PFWAIT,"PFSVC: ProcessTraceThread()-EndWaitForTrace=%x\n", WaitResult));
  619. switch(WaitResult) {
  620. case WAIT_OBJECT_0:
  621. //
  622. // Service exit event:
  623. //
  624. ErrorCode = ERROR_SUCCESS;
  625. goto cleanup;
  626. break;
  627. case WAIT_OBJECT_0 + 1:
  628. //
  629. // New traces queued for processing event:
  630. //
  631. break;
  632. case WAIT_OBJECT_0 + 2:
  633. //
  634. // Idle detection was overriden. If we had registered tasks
  635. // to be run, we will unregister them and run them manually.
  636. //
  637. PfSvSaveTraceProcessingStatistics(PfSvcGlobals.ServiceDataKey);
  638. if (LayoutTask.Registered) {
  639. PfSvUnregisterTask(&LayoutTask, FALSE);
  640. PfSvCleanupTask(&LayoutTask);
  641. PfSvInitializeTask(&LayoutTask);
  642. PfSvUpdateOptimalLayout(NULL);
  643. }
  644. if (DirectoryCleanupTask.Registered) {
  645. PfSvUnregisterTask(&DirectoryCleanupTask, FALSE);
  646. PfSvCleanupTask(&DirectoryCleanupTask);
  647. PfSvInitializeTask(&DirectoryCleanupTask);
  648. PfSvCleanupPrefetchDirectory(NULL);
  649. }
  650. //
  651. // We will drop out of this block, check & process queued traces
  652. // and then set the processing complete event.
  653. //
  654. break;
  655. default:
  656. //
  657. // Something went wrong...
  658. //
  659. ErrorCode = ERROR_INVALID_HANDLE;
  660. goto cleanup;
  661. }
  662. }
  663. //
  664. // Loop to check if there are new traces.
  665. //
  666. }
  667. //
  668. // We should not break out of the loop.
  669. //
  670. PFSVC_ASSERT(FALSE);
  671. ErrorCode = ERROR_INVALID_FUNCTION;
  672. cleanup:
  673. if (AcquiredTracesLock) {
  674. PFSVC_RELEASE_LOCK(PfSvcGlobals.TracesLock);
  675. }
  676. if (TraceBuffer) {
  677. VirtualFree(TraceBuffer, 0, MEM_RELEASE);
  678. }
  679. PfSvUnregisterTask(&LayoutTask, FALSE);
  680. PfSvCleanupTask(&LayoutTask);
  681. PfSvUnregisterTask(&DirectoryCleanupTask, FALSE);
  682. PfSvCleanupTask(&DirectoryCleanupTask);
  683. DBGPR((PFID,PFTRC,"PFSVC: ProcessTraceThread()=%x\n", ErrorCode));
  684. return ErrorCode;
  685. }
  686. DWORD
  687. WINAPI
  688. PfSvPollShellReadyWorker(
  689. VOID *Param
  690. )
  691. /*++
  692. Routine Description:
  693. This is the routine for the thread that is spawned to poll the
  694. ShellReadyEvent.
  695. Arguments:
  696. Param - Ignored.
  697. Return Value:
  698. Win32 error code.
  699. --*/
  700. {
  701. HANDLE ShellReadyEvent;
  702. HANDLE Events[2];
  703. ULONG NumEvents;
  704. ULONG PollPeriod;
  705. ULONG TotalPollPeriod;
  706. DWORD WaitResult;
  707. DWORD ErrorCode;
  708. NTSTATUS Status;
  709. PREFETCHER_INFORMATION PrefetcherInformation;
  710. PF_BOOT_PHASE_ID PhaseId;
  711. //
  712. // Initialize locals.
  713. //
  714. ShellReadyEvent = NULL;
  715. Events[0] = PfSvcGlobals.TerminateServiceEvent;
  716. NumEvents = 1;
  717. DBGPR((PFID,PFTRC,"PFSVC: PollShellReadyThread()\n"));
  718. //
  719. // Get necessary permissions for this thread to perform prefetch
  720. // service tasks.
  721. //
  722. ErrorCode = PfSvGetPrefetchServiceThreadPrivileges();
  723. if (ErrorCode != ERROR_SUCCESS) {
  724. goto cleanup;
  725. }
  726. //
  727. // Until we can open the shell ready event, wait on the service
  728. // termination event and retry every PollPeriod milliseconds.
  729. //
  730. PollPeriod = 1000;
  731. TotalPollPeriod = 0;
  732. do {
  733. //
  734. // Try to open the shell ready event.
  735. //
  736. // FUTURE-2002/03/29-ScottMa -- The EVENT_ALL_ACCESS flag only needs
  737. // to be SYNCHRONIZE, following the principle of least privelege.
  738. ShellReadyEvent = OpenEvent(EVENT_ALL_ACCESS,FALSE,L"ShellReadyEvent");
  739. if (ShellReadyEvent) {
  740. break;
  741. }
  742. //
  743. // Wait for a while.
  744. //
  745. DBGPR((PFID,PFWAIT,"PFSVC: PollShellReadyThread()-WaitForOpen\n"));
  746. WaitResult = WaitForMultipleObjects(NumEvents,
  747. Events,
  748. FALSE,
  749. PollPeriod);
  750. DBGPR((PFID,PFWAIT,"PFSVC: PollShellReadyThread()-EndWaitForOpen=%d\n", WaitResult));
  751. switch(WaitResult) {
  752. case WAIT_OBJECT_0:
  753. //
  754. // Service exit event:
  755. //
  756. ErrorCode = ERROR_PROCESS_ABORTED;
  757. goto cleanup;
  758. break;
  759. case WAIT_TIMEOUT:
  760. //
  761. // Fall through and try opening the shell ready event again.
  762. //
  763. break;
  764. default:
  765. //
  766. // Something gone wrong. Break out, cleanup and exit.
  767. //
  768. ErrorCode = ERROR_INVALID_HANDLE;
  769. goto cleanup;
  770. }
  771. TotalPollPeriod += PollPeriod;
  772. } while (TotalPollPeriod < 180000);
  773. //
  774. // If we could not get the ShellReadyEvent, we timed out.
  775. //
  776. if (ShellReadyEvent == NULL) {
  777. ErrorCode = ERROR_TIMEOUT;
  778. goto cleanup;
  779. }
  780. //
  781. // Wait on the ShellReadyEvent to be signaled.
  782. //
  783. Events[NumEvents] = ShellReadyEvent;
  784. NumEvents++;
  785. DBGPR((PFID,PFWAIT,"PFSVC: PollShellReadyThread()-WaitForShell\n"));
  786. WaitResult = WaitForMultipleObjects(NumEvents,
  787. Events,
  788. FALSE,
  789. 60000);
  790. DBGPR((PFID,PFWAIT,"PFSVC: PollShellReadyThread()-EndWaitForShell=%d\n",WaitResult));
  791. switch (WaitResult) {
  792. case WAIT_OBJECT_0:
  793. //
  794. // Service exit event:
  795. //
  796. ErrorCode = ERROR_PROCESS_ABORTED;
  797. goto cleanup;
  798. break;
  799. case WAIT_OBJECT_0 + 1:
  800. //
  801. // Shell ready event got signaled. Let the kernel mode
  802. // prefetcher know.
  803. //
  804. PhaseId = PfUserShellReadyPhase;
  805. PrefetcherInformation.Magic = PF_SYSINFO_MAGIC_NUMBER;
  806. PrefetcherInformation.Version = PF_CURRENT_VERSION;
  807. PrefetcherInformation.PrefetcherInformationClass = PrefetcherBootPhase;
  808. PrefetcherInformation.PrefetcherInformation = &PhaseId;
  809. PrefetcherInformation.PrefetcherInformationLength = sizeof(PhaseId);
  810. Status = NtSetSystemInformation(SystemPrefetcherInformation,
  811. &PrefetcherInformation,
  812. sizeof(PrefetcherInformation));
  813. //
  814. // Fall through with the status.
  815. //
  816. ErrorCode = RtlNtStatusToDosError(Status);
  817. break;
  818. case WAIT_TIMEOUT:
  819. //
  820. // Shell ready event was created but not signaled...
  821. //
  822. ErrorCode = ERROR_TIMEOUT;
  823. break;
  824. default:
  825. //
  826. // Something gone wrong.
  827. //
  828. ErrorCode = GetLastError();
  829. if (ErrorCode == ERROR_SUCCESS) {
  830. ErrorCode = ERROR_INVALID_FUNCTION;
  831. }
  832. }
  833. //
  834. // Fall through with status from the switch statement.
  835. //
  836. cleanup:
  837. if (ShellReadyEvent) {
  838. CloseHandle(ShellReadyEvent);
  839. }
  840. DBGPR((PFID,PFTRC,"PFSVC: PollShellReadyThread()=%x\n", ErrorCode));
  841. return ErrorCode;
  842. }
  843. //
  844. // Routines called by the main prefetcher thread.
  845. //
  846. DWORD
  847. PfSvGetRawTraces(
  848. VOID
  849. )
  850. /*++
  851. Routine Description:
  852. This routine checks for new traces prepared by the kernel. The new
  853. traces are downloaded and queued so they can be processed.
  854. Arguments:
  855. None.
  856. Return Value:
  857. Win32 error code.
  858. --*/
  859. {
  860. DWORD ErrorCode;
  861. NTSTATUS Status;
  862. PPFSVC_TRACE_BUFFER TraceBuffer;
  863. ULONG TraceBufferMaximumLength;
  864. ULONG TraceBufferLength;
  865. PREFETCHER_INFORMATION PrefetcherInformation;
  866. ULONG NumTracesRetrieved;
  867. ULONG FailedCheck;
  868. //
  869. // Initialize locals.
  870. //
  871. TraceBuffer = NULL;
  872. TraceBufferMaximumLength = 0;
  873. NumTracesRetrieved = 0;
  874. DBGPR((PFID,PFTRC,"PFSVC: GetRawTraces()\n"));
  875. //
  876. // Clear the event that asks us to check for more traces.
  877. //
  878. ResetEvent(PfSvcGlobals.CheckForMissedTracesEvent);
  879. //
  880. // While we do not already have too many traces to process, get
  881. // traces from the kernel.
  882. //
  883. while (PfSvcGlobals.NumTraces < PFSVC_MAX_NUM_QUEUED_TRACES) {
  884. //
  885. // Retrieve a trace from the kernel.
  886. //
  887. PrefetcherInformation.Version = PF_CURRENT_VERSION;
  888. PrefetcherInformation.Magic = PF_SYSINFO_MAGIC_NUMBER;
  889. PrefetcherInformation.PrefetcherInformationClass = PrefetcherRetrieveTrace;
  890. PrefetcherInformation.PrefetcherInformation = &TraceBuffer->Trace;
  891. if (TraceBufferMaximumLength <= FIELD_OFFSET(PFSVC_TRACE_BUFFER, Trace)) {
  892. PrefetcherInformation.PrefetcherInformationLength = 0;
  893. } else {
  894. PrefetcherInformation.PrefetcherInformationLength =
  895. TraceBufferMaximumLength - FIELD_OFFSET(PFSVC_TRACE_BUFFER, Trace);
  896. }
  897. Status = NtQuerySystemInformation(SystemPrefetcherInformation,
  898. &PrefetcherInformation,
  899. sizeof(PrefetcherInformation),
  900. &TraceBufferLength);
  901. if (!NT_SUCCESS(Status)) {
  902. if (Status == STATUS_BUFFER_TOO_SMALL) {
  903. if (TraceBuffer != NULL) {
  904. VirtualFree(TraceBuffer, 0, MEM_RELEASE);
  905. }
  906. //
  907. // Add room for the header we wrap over it.
  908. //
  909. TraceBufferLength += sizeof(PFSVC_TRACE_BUFFER) - sizeof(PF_TRACE_HEADER);
  910. TraceBufferMaximumLength = ROUND_TRACE_BUFFER_SIZE(TraceBufferLength);
  911. TraceBuffer = VirtualAlloc(NULL,
  912. TraceBufferMaximumLength,
  913. MEM_COMMIT,
  914. PAGE_READWRITE);
  915. if (TraceBuffer == NULL) {
  916. ErrorCode = GetLastError();
  917. goto cleanup;
  918. }
  919. continue;
  920. } else if (Status == STATUS_NO_MORE_ENTRIES) {
  921. break;
  922. }
  923. ErrorCode = RtlNtStatusToDosError(Status);
  924. goto cleanup;
  925. }
  926. #ifdef PFSVC_DBG
  927. //
  928. // Write out the trace to a file:
  929. //
  930. if (PfSvcDbgMaxNumSavedTraces) {
  931. WCHAR TraceFilePath[MAX_PATH + 1];
  932. LONG NumChars;
  933. //
  934. // Build up a file name.
  935. //
  936. InterlockedIncrement(&PfSvcDbgTraceNumber);
  937. PFSVC_ACQUIRE_LOCK(PfSvcGlobals.PrefetchRootLock);
  938. NumChars = _snwprintf(TraceFilePath,
  939. MAX_PATH,
  940. L"%ws\\%ws%d.trc",
  941. PfSvcGlobals.PrefetchRoot,
  942. PfSvcDbgTraceBaseName,
  943. PfSvcDbgTraceNumber % PfSvcDbgMaxNumSavedTraces);
  944. PFSVC_RELEASE_LOCK(PfSvcGlobals.PrefetchRootLock);
  945. if (NumChars > 0 && NumChars < MAX_PATH) {
  946. //
  947. // Make sure the path is terminated.
  948. //
  949. TraceFilePath[MAX_PATH - 1] = 0;
  950. //
  951. // Write out the trace.
  952. //
  953. PfSvWriteBuffer(TraceFilePath,
  954. &TraceBuffer->Trace,
  955. TraceBuffer->Trace.Size);
  956. }
  957. }
  958. #endif // PFSVC_DBG
  959. //
  960. // Verify integrity of the trace.
  961. //
  962. if (!PfVerifyTraceBuffer(&TraceBuffer->Trace,
  963. TraceBuffer->Trace.Size,
  964. &FailedCheck)) {
  965. DBGPR((PFID,PFWARN,"PFSVC: IGNORING TRACE\n"));
  966. continue;
  967. }
  968. //
  969. // Put it on the list of traces to process.
  970. //
  971. PFSVC_ACQUIRE_LOCK(PfSvcGlobals.TracesLock);
  972. InsertTailList(&PfSvcGlobals.Traces, &TraceBuffer->TracesLink);
  973. PfSvcGlobals.NumTraces++;
  974. PFSVC_RELEASE_LOCK(PfSvcGlobals.TracesLock);
  975. //
  976. // Notify that there are new traces to process.
  977. //
  978. SetEvent(PfSvcGlobals.NewTracesToProcessEvent);
  979. //
  980. // Clean out the loop variables.
  981. //
  982. TraceBuffer = NULL;
  983. TraceBufferMaximumLength = 0;
  984. TraceBufferLength = 0;
  985. NumTracesRetrieved++;
  986. }
  987. //
  988. // We should never go above the limit of queued traces.
  989. //
  990. PFSVC_ASSERT(PfSvcGlobals.NumTraces <= PFSVC_MAX_NUM_QUEUED_TRACES);
  991. ErrorCode = ERROR_SUCCESS;
  992. cleanup:
  993. if (TraceBuffer != NULL) {
  994. VirtualFree(TraceBuffer, 0, MEM_RELEASE);
  995. }
  996. DBGPR((PFID,PFTRC,"PFSVC: GetRawTraces()=%x,%d\n", ErrorCode, NumTracesRetrieved));
  997. return ErrorCode;
  998. }
  999. DWORD
  1000. PfSvInitializeGlobals(
  1001. VOID
  1002. )
  1003. /*++
  1004. Routine Description:
  1005. This routine initializes the global variables / tables etc.
  1006. Arguments:
  1007. None.
  1008. Return Value:
  1009. Win32 error code.
  1010. --*/
  1011. {
  1012. NTSTATUS Status;
  1013. DWORD ErrorCode;
  1014. ULONG FileIdx;
  1015. WCHAR *CSCRootPath;
  1016. ULONG CSCRootPathMaxChars;
  1017. //
  1018. // These are the path suffices to recognize files we don't want to
  1019. // prefetch for boot. Keep these sorted lexically going from
  1020. // LAST CHARACTER TO FIRST and UPCASE.
  1021. //
  1022. static WCHAR *FilesToIgnoreForBoot[] = {
  1023. L"SYSTEM32\\CONFIG\\SOFTWARE",
  1024. L"\\WMI\\TRACE.LOG",
  1025. L"SYSTEM32\\CONFIG\\SOFTWARE.LOG",
  1026. L"SYSTEM32\\CONFIG\\SAM.LOG",
  1027. L"SYSTEM32\\CONFIG\\SYSTEM.LOG",
  1028. L"SYSTEM32\\CONFIG\\DEFAULT.LOG",
  1029. L"SYSTEM32\\CONFIG\\SECURITY.LOG",
  1030. L"\\PERF.ETL",
  1031. L"SYSTEM32\\CONFIG\\SAM",
  1032. L"SYSTEM32\\CONFIG\\SYSTEM",
  1033. L"SYSTEM32\\CONFIG\\SYSTEM.ALT",
  1034. L"SYSTEM32\\CONFIG\\DEFAULT",
  1035. L"SYSTEM32\\CONFIG\\SECURITY",
  1036. };
  1037. DBGPR((PFID,PFTRC,"PFSVC: InitializeGlobals()\n"));
  1038. //
  1039. // Initialize locals.
  1040. //
  1041. CSCRootPath = NULL;
  1042. //
  1043. // Zero out the globals structure so we know what to cleanup if
  1044. // the initialization fails in the middle.
  1045. //
  1046. RtlZeroMemory(&PfSvcGlobals, sizeof(PfSvcGlobals));
  1047. //
  1048. // Initialize the list of traces to be processed.
  1049. //
  1050. InitializeListHead(&PfSvcGlobals.Traces);
  1051. PfSvcGlobals.NumTraces = 0;
  1052. //
  1053. // We have not launched the defragger for anything yet.
  1054. //
  1055. PfSvcGlobals.DefraggerErrorCode = ERROR_SUCCESS;
  1056. //
  1057. // Initialize table for registry files that we don't want to
  1058. // prefetch for boot.
  1059. //
  1060. PfSvcGlobals.FilesToIgnoreForBoot = FilesToIgnoreForBoot;
  1061. PfSvcGlobals.NumFilesToIgnoreForBoot =
  1062. sizeof(FilesToIgnoreForBoot) / sizeof(WCHAR *);
  1063. //
  1064. // Get OS version information.
  1065. //
  1066. RtlZeroMemory(&PfSvcGlobals.OsVersion, sizeof(PfSvcGlobals.OsVersion));
  1067. PfSvcGlobals.OsVersion.dwOSVersionInfoSize = sizeof(PfSvcGlobals.OsVersion);
  1068. Status = RtlGetVersion((PRTL_OSVERSIONINFOW)&PfSvcGlobals.OsVersion);
  1069. if (!NT_SUCCESS(Status)) {
  1070. DBGPR((PFID,PFERR,"PFSVC: MainThread()-FailedGetOSVersion\n"));
  1071. ErrorCode = RtlNtStatusToDosError(Status);
  1072. goto cleanup;
  1073. }
  1074. //
  1075. // Initialize the table of ignored files' suffix lengths.
  1076. //
  1077. PfSvcGlobals.FileSuffixLengths =
  1078. PFSVC_ALLOC(PfSvcGlobals.NumFilesToIgnoreForBoot * sizeof(ULONG));
  1079. if (!PfSvcGlobals.FileSuffixLengths) {
  1080. ErrorCode = ERROR_NOT_ENOUGH_MEMORY;
  1081. goto cleanup;
  1082. }
  1083. for (FileIdx = 0;
  1084. FileIdx < PfSvcGlobals.NumFilesToIgnoreForBoot;
  1085. FileIdx++) {
  1086. PfSvcGlobals.FileSuffixLengths[FileIdx] =
  1087. wcslen(PfSvcGlobals.FilesToIgnoreForBoot[FileIdx]);
  1088. }
  1089. //
  1090. // Create an event that will get signaled when the service is
  1091. // exiting.
  1092. //
  1093. PfSvcGlobals.TerminateServiceEvent = CreateEvent(NULL,
  1094. TRUE,
  1095. FALSE,
  1096. NULL);
  1097. if (PfSvcGlobals.TerminateServiceEvent == NULL) {
  1098. ErrorCode = GetLastError();
  1099. goto cleanup;
  1100. }
  1101. //
  1102. // Initialize the lock for the list of traces to be processed.
  1103. //
  1104. PfSvcGlobals.TracesLock = CreateMutex(NULL, FALSE, NULL);
  1105. if (PfSvcGlobals.TracesLock == NULL) {
  1106. ErrorCode = GetLastError();
  1107. goto cleanup;
  1108. }
  1109. //
  1110. // Initialize the events that are used to communicate between the
  1111. // acquirer and processor of the traces.
  1112. //
  1113. PfSvcGlobals.NewTracesToProcessEvent = CreateEvent(NULL,
  1114. FALSE,
  1115. FALSE,
  1116. NULL);
  1117. if (PfSvcGlobals.NewTracesToProcessEvent == NULL) {
  1118. ErrorCode = GetLastError();
  1119. goto cleanup;
  1120. }
  1121. PfSvcGlobals.CheckForMissedTracesEvent = CreateEvent(NULL,
  1122. FALSE,
  1123. FALSE,
  1124. NULL);
  1125. if (PfSvcGlobals.CheckForMissedTracesEvent == NULL) {
  1126. ErrorCode = GetLastError();
  1127. goto cleanup;
  1128. }
  1129. //
  1130. // This named manual-reset event can be set to force all traces to
  1131. // be processed as soon as they become available rather than
  1132. // waiting for the system to become idle first.
  1133. //
  1134. // NOTICE-2002/03/29-ScottMa -- Have we considered the impact of squatting
  1135. // on all the named events used in the code?
  1136. PfSvcGlobals.OverrideIdleProcessingEvent = CreateEvent(NULL,
  1137. TRUE,
  1138. FALSE,
  1139. PFSVC_OVERRIDE_IDLE_EVENT_NAME);
  1140. if (PfSvcGlobals.OverrideIdleProcessingEvent == NULL) {
  1141. ErrorCode = GetLastError();
  1142. goto cleanup;
  1143. }
  1144. //
  1145. // This named manual-reset event is created signaled. When this
  1146. // event is signaled, it means there are no traces we have to
  1147. // process now.
  1148. //
  1149. PfSvcGlobals.ProcessingCompleteEvent = CreateEvent(NULL,
  1150. TRUE,
  1151. TRUE,
  1152. PFSVC_PROCESSING_COMPLETE_EVENT_NAME);
  1153. if (PfSvcGlobals.ProcessingCompleteEvent == NULL) {
  1154. ErrorCode = GetLastError();
  1155. goto cleanup;
  1156. }
  1157. //
  1158. // Initialize prefetch root path and the lock to protect it. The
  1159. // real root path will be initialized after parameters are queried
  1160. // from the kernel.
  1161. //
  1162. PfSvcGlobals.PrefetchRoot[0] = 0;
  1163. PfSvcGlobals.PrefetchRootLock = CreateMutex(NULL, FALSE, NULL);
  1164. if (PfSvcGlobals.PrefetchRootLock == NULL) {
  1165. ErrorCode = GetLastError();
  1166. goto cleanup;
  1167. }
  1168. PfSvcGlobals.NumPrefetchFiles = 0;
  1169. //
  1170. // Open the service data registry key, creating it if necessary.
  1171. //
  1172. ErrorCode = RegCreateKey(HKEY_LOCAL_MACHINE,
  1173. PFSVC_SERVICE_DATA_KEY,
  1174. &PfSvcGlobals.ServiceDataKey);
  1175. if (ErrorCode != ERROR_SUCCESS) {
  1176. goto cleanup;
  1177. }
  1178. //
  1179. // Check the registry to see if the user does not want us to run
  1180. // the defragger.
  1181. //
  1182. ErrorCode = PfSvGetDontRunDefragger(&PfSvcGlobals.DontRunDefragger);
  1183. if (ErrorCode != ERROR_SUCCESS) {
  1184. //
  1185. // By default we will run the defragger.
  1186. //
  1187. PfSvcGlobals.DontRunDefragger = FALSE;
  1188. }
  1189. //
  1190. // Determine CSC root path. It won't be used if we can't allocate or
  1191. // determine it, so don't worry about the error code.
  1192. //
  1193. CSCRootPathMaxChars = MAX_PATH + 1;
  1194. CSCRootPath = PFSVC_ALLOC(CSCRootPathMaxChars * sizeof(CSCRootPath[0]));
  1195. if (CSCRootPath) {
  1196. ErrorCode = PfSvGetCSCRootPath(CSCRootPath, CSCRootPathMaxChars);
  1197. if (ErrorCode == ERROR_SUCCESS) {
  1198. PfSvcGlobals.CSCRootPath = CSCRootPath;
  1199. CSCRootPath = NULL;
  1200. }
  1201. }
  1202. //
  1203. // We are done.
  1204. //
  1205. ErrorCode = ERROR_SUCCESS;
  1206. cleanup:
  1207. DBGPR((PFID,PFTRC,"PFSVC: InitializeGlobals()=%x\n", ErrorCode));
  1208. if (CSCRootPath) {
  1209. PFSVC_FREE(CSCRootPath);
  1210. }
  1211. return ErrorCode;
  1212. }
  1213. VOID
  1214. PfSvCleanupGlobals(
  1215. VOID
  1216. )
  1217. /*++
  1218. Routine Description:
  1219. This routine uninitializes the global variables / tables etc.
  1220. Arguments:
  1221. None.
  1222. Return Value:
  1223. VOID
  1224. --*/
  1225. {
  1226. PPFSVC_TRACE_BUFFER TraceBuffer;
  1227. PLIST_ENTRY ListHead;
  1228. DBGPR((PFID,PFTRC,"PFSVC: CleanupGlobals()\n"));
  1229. //
  1230. // Free allocated table.
  1231. //
  1232. if (PfSvcGlobals.FileSuffixLengths) {
  1233. PFSVC_FREE(PfSvcGlobals.FileSuffixLengths);
  1234. }
  1235. //
  1236. // Free queued traces.
  1237. //
  1238. while (!IsListEmpty(&PfSvcGlobals.Traces)) {
  1239. ListHead = RemoveHeadList(&PfSvcGlobals.Traces);
  1240. PFSVC_ASSERT(PfSvcGlobals.NumTraces);
  1241. PfSvcGlobals.NumTraces--;
  1242. TraceBuffer = CONTAINING_RECORD(ListHead,
  1243. PFSVC_TRACE_BUFFER,
  1244. TracesLink);
  1245. VirtualFree(TraceBuffer, 0, MEM_RELEASE);
  1246. }
  1247. //
  1248. // Close handles to opened events/mutexes.
  1249. //
  1250. if (PfSvcGlobals.TerminateServiceEvent) {
  1251. CloseHandle(PfSvcGlobals.TerminateServiceEvent);
  1252. }
  1253. if (PfSvcGlobals.TracesLock) {
  1254. CloseHandle(PfSvcGlobals.TracesLock);
  1255. }
  1256. if (PfSvcGlobals.NewTracesToProcessEvent) {
  1257. CloseHandle(PfSvcGlobals.NewTracesToProcessEvent);
  1258. }
  1259. if (PfSvcGlobals.CheckForMissedTracesEvent) {
  1260. CloseHandle(PfSvcGlobals.CheckForMissedTracesEvent);
  1261. }
  1262. if (PfSvcGlobals.OverrideIdleProcessingEvent) {
  1263. CloseHandle(PfSvcGlobals.OverrideIdleProcessingEvent);
  1264. }
  1265. if (PfSvcGlobals.ProcessingCompleteEvent) {
  1266. CloseHandle(PfSvcGlobals.ProcessingCompleteEvent);
  1267. }
  1268. if (PfSvcGlobals.PrefetchRootLock) {
  1269. CloseHandle(PfSvcGlobals.PrefetchRootLock);
  1270. }
  1271. //
  1272. // Close service data key handle.
  1273. //
  1274. if (PfSvcGlobals.ServiceDataKey) {
  1275. RegCloseKey(PfSvcGlobals.ServiceDataKey);
  1276. }
  1277. //
  1278. // Free CSC root path.
  1279. //
  1280. if (PfSvcGlobals.CSCRootPath) {
  1281. PFSVC_FREE(PfSvcGlobals.CSCRootPath);
  1282. }
  1283. }
  1284. DWORD
  1285. PfSvGetCSCRootPath (
  1286. WCHAR *CSCRootPath,
  1287. ULONG CSCRootPathMaxChars
  1288. )
  1289. /*++
  1290. Routine Description:
  1291. This routine determines the root path for CSC (client side caching) files.
  1292. Arguments:
  1293. CSCRootPath - If successful, a NUL terminated string is copied into this buffer.
  1294. CSCRootPathMaxChars - Maximum characters we can copy into CSCRootPath
  1295. buffer including the terminating NUL.
  1296. Return Value:
  1297. Win32 error code.
  1298. --*/
  1299. {
  1300. WCHAR CSCDirName[] = L"CSC";
  1301. HKEY CSCKeyHandle;
  1302. ULONG WindowsDirectoryLength;
  1303. ULONG CSCRootPathLength;
  1304. ULONG RequiredNumChars;
  1305. DWORD ErrorCode;
  1306. DWORD BufferSize;
  1307. DWORD ValueType;
  1308. //
  1309. // Initialize locals.
  1310. //
  1311. CSCKeyHandle = NULL;
  1312. //
  1313. // Open CSC parameters key.
  1314. //
  1315. ErrorCode = RegOpenKey(HKEY_LOCAL_MACHINE,
  1316. TEXT(REG_STRING_NETCACHE_KEY_A),
  1317. &CSCKeyHandle);
  1318. if (ErrorCode == ERROR_SUCCESS) {
  1319. //
  1320. // Query system setting for the CSC root path.
  1321. //
  1322. BufferSize = CSCRootPathMaxChars * sizeof(CSCRootPath[0]);
  1323. ErrorCode = RegQueryValueEx(CSCKeyHandle,
  1324. TEXT(REG_STRING_DATABASE_LOCATION_A),
  1325. NULL,
  1326. &ValueType,
  1327. (PVOID)CSCRootPath,
  1328. &BufferSize);
  1329. if (ErrorCode == ERROR_SUCCESS) {
  1330. //
  1331. // Sanity check the length.
  1332. //
  1333. if ((BufferSize / sizeof(CSCRootPath[0])) < MAX_PATH) {
  1334. //
  1335. // Make sure the buffer is NUL terminated.
  1336. //
  1337. CSCRootPath[CSCRootPathMaxChars-1] = 0;
  1338. //
  1339. // We got what we wanted. Make sure it has room for and is terminated
  1340. // by a slash.
  1341. //
  1342. CSCRootPathLength = wcslen(CSCRootPath);
  1343. if (CSCRootPathLength < CSCRootPathMaxChars - 1) {
  1344. if (CSCRootPath[CSCRootPathLength - 1] != L'\\') {
  1345. CSCRootPath[CSCRootPathLength] = L'\\';
  1346. CSCRootPathLength++;
  1347. CSCRootPath[CSCRootPathLength] = L'\0';
  1348. }
  1349. ErrorCode = ERROR_SUCCESS;
  1350. goto cleanup;
  1351. }
  1352. }
  1353. }
  1354. }
  1355. //
  1356. // If we come here, we have to use the default CSC path i.e. %windir%\CSC
  1357. //
  1358. WindowsDirectoryLength = GetWindowsDirectory(CSCRootPath,
  1359. CSCRootPathMaxChars - 1);
  1360. if (WindowsDirectoryLength == 0) {
  1361. //
  1362. // There was an error.
  1363. //
  1364. ErrorCode = GetLastError();
  1365. PFSVC_ASSERT(ErrorCode != ERROR_SUCCESS);
  1366. goto cleanup;
  1367. }
  1368. //
  1369. // See if we have room to add \CSC\ and a terminating NUL.
  1370. //
  1371. RequiredNumChars = WindowsDirectoryLength;
  1372. RequiredNumChars ++; // leading backslash.
  1373. RequiredNumChars += wcslen(CSCDirName); // CSC.
  1374. RequiredNumChars ++; // ending backslash.
  1375. RequiredNumChars ++; // terminating NUL.
  1376. if (CSCRootPathMaxChars < RequiredNumChars) {
  1377. ErrorCode = ERROR_INSUFFICIENT_BUFFER;
  1378. goto cleanup;
  1379. }
  1380. //
  1381. // Build up the path:
  1382. //
  1383. CSCRootPathLength = WindowsDirectoryLength;
  1384. if (CSCRootPath[CSCRootPathLength - 1] != L'\\') {
  1385. CSCRootPath[CSCRootPathLength] = L'\\';
  1386. CSCRootPathLength++;
  1387. }
  1388. wcscpy(CSCRootPath + CSCRootPathLength, CSCDirName);
  1389. CSCRootPathLength += wcslen(CSCDirName);
  1390. CSCRootPath[CSCRootPathLength] = L'\\';
  1391. CSCRootPathLength++;
  1392. //
  1393. // Terminate the string.
  1394. //
  1395. CSCRootPath[CSCRootPathLength] = L'\0';
  1396. //
  1397. // We are done.
  1398. //
  1399. ErrorCode = ERROR_SUCCESS;
  1400. cleanup:
  1401. if (CSCKeyHandle) {
  1402. RegCloseKey(CSCKeyHandle);
  1403. }
  1404. if (ErrorCode == ERROR_SUCCESS) {
  1405. //
  1406. // We have the path in CSCRootPath. It should be in the X:\path\
  1407. // format. It should also be somewhat long, otherwise we will mismatch
  1408. // to too many files that we will not prefetch. It should also be
  1409. // terminated by a \ and NUL.
  1410. //
  1411. PFSVC_ASSERT(CSCRootPathLength < CSCRootPathMaxChars);
  1412. if ((CSCRootPathLength > 6) &&
  1413. (CSCRootPath[1] == L':') &&
  1414. (CSCRootPath[2] == L'\\') &&
  1415. (CSCRootPath[CSCRootPathLength - 1] == L'\\') &&
  1416. (CSCRootPath[CSCRootPathLength] == L'\0')) {
  1417. //
  1418. // Remove the X: from the beginning of the path so we can match
  1419. // it to NT paths like \Device\HarddiskVolume1. Note that we have
  1420. // to move the terminating NUL too.
  1421. //
  1422. MoveMemory(CSCRootPath,
  1423. CSCRootPath + 2,
  1424. (CSCRootPathLength - 1) * sizeof(CSCRootPath[0]));
  1425. CSCRootPathLength -= 2;
  1426. //
  1427. // Upcase the path so we don't have to do expensive case insensitive
  1428. // comparisons.
  1429. //
  1430. _wcsupr(CSCRootPath);
  1431. } else {
  1432. ErrorCode = ERROR_BAD_FORMAT;
  1433. }
  1434. }
  1435. return ErrorCode;
  1436. }
  1437. DWORD
  1438. PfSvSetPrefetchParameters(
  1439. PPF_SYSTEM_PREFETCH_PARAMETERS Parameters
  1440. )
  1441. /*++
  1442. Routine Description:
  1443. This routine updates the system prefetch parameters in the kernel.
  1444. Arguments:
  1445. Parameters - Pointer to parameters structure.
  1446. Return Value:
  1447. Win32 error code.
  1448. --*/
  1449. {
  1450. PREFETCHER_INFORMATION PrefetcherInformation;
  1451. NTSTATUS Status;
  1452. DWORD ErrorCode;
  1453. PrefetcherInformation.Magic = PF_SYSINFO_MAGIC_NUMBER;
  1454. PrefetcherInformation.Version = PF_CURRENT_VERSION;
  1455. PrefetcherInformation.PrefetcherInformationClass = PrefetcherSystemParameters;
  1456. PrefetcherInformation.PrefetcherInformation = Parameters;
  1457. PrefetcherInformation.PrefetcherInformationLength = sizeof(*Parameters);
  1458. Status = NtSetSystemInformation(SystemPrefetcherInformation,
  1459. &PrefetcherInformation,
  1460. sizeof(PrefetcherInformation));
  1461. if (!NT_SUCCESS(Status)) {
  1462. ErrorCode = RtlNtStatusToDosError(Status);
  1463. goto cleanup;
  1464. }
  1465. ErrorCode = ERROR_SUCCESS;
  1466. cleanup:
  1467. return ErrorCode;
  1468. }
  1469. DWORD
  1470. PfSvQueryPrefetchParameters(
  1471. PPF_SYSTEM_PREFETCH_PARAMETERS Parameters
  1472. )
  1473. /*++
  1474. Routine Description:
  1475. This routine queries the system prefetch parameters from the kernel.
  1476. The calling thread must have called PfSvGetPrefetchServiceThreadPrivileges.
  1477. Arguments:
  1478. Parameters - Pointer to structure to update.
  1479. Return Value:
  1480. Win32 error code.
  1481. --*/
  1482. {
  1483. PREFETCHER_INFORMATION PrefetcherInformation;
  1484. NTSTATUS Status;
  1485. DWORD ErrorCode;
  1486. ULONG Length;
  1487. PrefetcherInformation.Magic = PF_SYSINFO_MAGIC_NUMBER;
  1488. PrefetcherInformation.Version = PF_CURRENT_VERSION;
  1489. PrefetcherInformation.PrefetcherInformationClass = PrefetcherSystemParameters;
  1490. PrefetcherInformation.PrefetcherInformation = Parameters;
  1491. PrefetcherInformation.PrefetcherInformationLength = sizeof(*Parameters);
  1492. Status = NtQuerySystemInformation(SystemPrefetcherInformation,
  1493. &PrefetcherInformation,
  1494. sizeof(PrefetcherInformation),
  1495. &Length);
  1496. if (!NT_SUCCESS(Status)) {
  1497. ErrorCode = RtlNtStatusToDosError(Status);
  1498. goto cleanup;
  1499. }
  1500. ErrorCode = ERROR_SUCCESS;
  1501. cleanup:
  1502. return ErrorCode;
  1503. }
  1504. DWORD
  1505. PfSvInitializePrefetchDirectory(
  1506. WCHAR *PathFromSystemRoot
  1507. )
  1508. /*++
  1509. Routine Description:
  1510. This routine builds up full path for the prefetch instructions
  1511. directory given PathFromSystemRoot, makes sure this directory
  1512. exists, and sets the security information on it. Finally, the
  1513. global PrefetchRoot path is updated with path to the new
  1514. directory.
  1515. Global NumPrefetchFiles is also updated.
  1516. The calling thread must have the SE_TAKE_OWNERSHIP_NAME privilege.
  1517. Arguments:
  1518. PathFromSystemRoot - Path to the prefetch directory from SystemRoot.
  1519. Return Value:
  1520. Win32 error code.
  1521. --*/
  1522. {
  1523. ULONG PathLength;
  1524. ULONG NumFiles;
  1525. HANDLE DirHandle;
  1526. DWORD ErrorCode;
  1527. DWORD FileAttributes;
  1528. WCHAR FullDirPathBuffer[MAX_PATH + 1];
  1529. //
  1530. // Initialize locals.
  1531. //
  1532. DirHandle = INVALID_HANDLE_VALUE;
  1533. DBGPR((PFID,PFTRC,"PFSVC: InitPrefetchDir(%ws)\n",PathFromSystemRoot));
  1534. //
  1535. // Build path name to the prefetch files directory.
  1536. // ExpandEnvironmentStrings return length includes space for
  1537. // the terminating NUL character.
  1538. //
  1539. PathLength = ExpandEnvironmentStrings(L"%SystemRoot%\\",
  1540. FullDirPathBuffer,
  1541. MAX_PATH);
  1542. PathLength += wcslen(PathFromSystemRoot);
  1543. if (PathLength > MAX_PATH) {
  1544. ErrorCode = ERROR_INSUFFICIENT_BUFFER;
  1545. goto cleanup;
  1546. }
  1547. //
  1548. // Copy the path from system root.
  1549. //
  1550. wcscat(FullDirPathBuffer, PathFromSystemRoot);
  1551. //
  1552. // Create the directory if it does not already exist.
  1553. //
  1554. if (!CreateDirectory(FullDirPathBuffer, NULL)) {
  1555. ErrorCode = GetLastError();
  1556. if (ErrorCode == ERROR_ALREADY_EXISTS) {
  1557. //
  1558. // The directory, or a file with that name may already
  1559. // exist. Make sure it is the former.
  1560. //
  1561. FileAttributes = GetFileAttributes(FullDirPathBuffer);
  1562. if (FileAttributes == INVALID_FILE_ATTRIBUTES) {
  1563. ErrorCode = GetLastError();
  1564. goto cleanup;
  1565. }
  1566. if (!(FileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
  1567. ErrorCode = ERROR_CANNOT_MAKE;
  1568. goto cleanup;
  1569. }
  1570. } else {
  1571. goto cleanup;
  1572. }
  1573. }
  1574. //
  1575. // Disable indexing of the prefetch directory.
  1576. //
  1577. FileAttributes = GetFileAttributes(FullDirPathBuffer);
  1578. if (FileAttributes == INVALID_FILE_ATTRIBUTES) {
  1579. ErrorCode = GetLastError();
  1580. goto cleanup;
  1581. }
  1582. if (!SetFileAttributes(FullDirPathBuffer,
  1583. FileAttributes | FILE_ATTRIBUTE_NOT_CONTENT_INDEXED)) {
  1584. ErrorCode = GetLastError();
  1585. goto cleanup;
  1586. }
  1587. //
  1588. // Set permissions.
  1589. //
  1590. ErrorCode = PfSvSetAdminOnlyPermissions(FullDirPathBuffer, NULL, SE_FILE_OBJECT);
  1591. if (ErrorCode != ERROR_SUCCESS) {
  1592. goto cleanup;
  1593. }
  1594. //
  1595. // Count the scenario files in the directory.
  1596. //
  1597. ErrorCode = PfSvCountFilesInDirectory(FullDirPathBuffer,
  1598. L"*." PF_PREFETCH_FILE_EXTENSION,
  1599. &NumFiles);
  1600. if (ErrorCode != ERROR_SUCCESS) {
  1601. goto cleanup;
  1602. }
  1603. //
  1604. // Update the global prefetch root directory path.
  1605. //
  1606. PFSVC_ACQUIRE_LOCK(PfSvcGlobals.PrefetchRootLock);
  1607. wcscpy(PfSvcGlobals.PrefetchRoot, FullDirPathBuffer);
  1608. PfSvcGlobals.NumPrefetchFiles = NumFiles;
  1609. PFSVC_RELEASE_LOCK(PfSvcGlobals.PrefetchRootLock);
  1610. ErrorCode = ERROR_SUCCESS;
  1611. cleanup:
  1612. DBGPR((PFID,PFTRC,"PFSVC: InitPrefetchDir(%ws)=%x\n",PathFromSystemRoot,ErrorCode));
  1613. return ErrorCode;
  1614. }
  1615. DWORD
  1616. PfSvCountFilesInDirectory(
  1617. WCHAR *DirectoryPath,
  1618. WCHAR *MatchExpression,
  1619. PULONG NumFiles
  1620. )
  1621. /*++
  1622. Routine Description:
  1623. This is routine returns the number of files in the specified
  1624. directory whose names match the specified expression.
  1625. Arguments:
  1626. DirectoryPath - NULL terminated path to the directory.
  1627. MatchExpression - Something like "*.pf" Don't go nuts with DOS
  1628. type expressions, this function won't try to transmogrify them.
  1629. NumFiles - Number of files are returned here. Bogus if returned error.
  1630. Return Value:
  1631. Win32 error code.
  1632. --*/
  1633. {
  1634. IO_STATUS_BLOCK IoStatusBlock;
  1635. OBJECT_ATTRIBUTES ObjectAttributes;
  1636. UNICODE_STRING DirectoryPathU;
  1637. UNICODE_STRING MatchExpressionU;
  1638. HANDLE DirectoryHandle;
  1639. PVOID QueryBuffer;
  1640. PFILE_NAMES_INFORMATION FileInfo;
  1641. ULONG QueryBufferSize;
  1642. ULONG FileCount;
  1643. NTSTATUS Status;
  1644. DWORD ErrorCode;
  1645. BOOLEAN Success;
  1646. BOOLEAN AllocatedDirectoryPathU;
  1647. BOOLEAN OpenedDirectory;
  1648. BOOLEAN RestartScan;
  1649. //
  1650. // Initialize locals.
  1651. //
  1652. AllocatedDirectoryPathU = FALSE;
  1653. OpenedDirectory = FALSE;
  1654. QueryBuffer = NULL;
  1655. QueryBufferSize = 0;
  1656. RtlInitUnicodeString(&MatchExpressionU, MatchExpression);
  1657. DBGPR((PFID,PFTRC,"PFSVC: CountFilesInDirectory(%ws,%ws)\n", DirectoryPath, MatchExpression));
  1658. //
  1659. // Convert the path to NT path.
  1660. //
  1661. Success = RtlDosPathNameToNtPathName_U(DirectoryPath,
  1662. &DirectoryPathU,
  1663. NULL,
  1664. NULL);
  1665. if (!Success) {
  1666. ErrorCode = ERROR_PATH_NOT_FOUND;
  1667. goto cleanup;
  1668. }
  1669. AllocatedDirectoryPathU = TRUE;
  1670. //
  1671. // Open the directory.
  1672. //
  1673. InitializeObjectAttributes(&ObjectAttributes,
  1674. &DirectoryPathU,
  1675. OBJ_CASE_INSENSITIVE,
  1676. NULL,
  1677. NULL);
  1678. Status = NtOpenFile(&DirectoryHandle,
  1679. FILE_LIST_DIRECTORY | SYNCHRONIZE,
  1680. &ObjectAttributes,
  1681. &IoStatusBlock,
  1682. FILE_SHARE_READ | FILE_SHARE_WRITE,
  1683. FILE_DIRECTORY_FILE |
  1684. FILE_SYNCHRONOUS_IO_NONALERT |
  1685. FILE_OPEN_FOR_BACKUP_INTENT);
  1686. if (!NT_SUCCESS(Status)) {
  1687. ErrorCode = RtlNtStatusToDosError(Status);
  1688. goto cleanup;
  1689. }
  1690. OpenedDirectory = TRUE;
  1691. //
  1692. // Allocate a decent sized query buffer.
  1693. //
  1694. QueryBufferSize = sizeof(FILE_NAMES_INFORMATION) + MAX_PATH * sizeof(WCHAR);
  1695. QueryBufferSize *= 16;
  1696. QueryBuffer = PFSVC_ALLOC(QueryBufferSize);
  1697. if (!QueryBuffer) {
  1698. ErrorCode = ERROR_NOT_ENOUGH_MEMORY;
  1699. goto cleanup;
  1700. }
  1701. //
  1702. // Loop querying file data. We query FileNamesInformation so
  1703. // we don't have to access file metadata.
  1704. //
  1705. RestartScan = TRUE;
  1706. FileCount = 0;
  1707. while (TRUE) {
  1708. Status = NtQueryDirectoryFile(DirectoryHandle,
  1709. NULL,
  1710. NULL,
  1711. NULL,
  1712. &IoStatusBlock,
  1713. QueryBuffer,
  1714. QueryBufferSize,
  1715. FileNamesInformation,
  1716. FALSE,
  1717. &MatchExpressionU,
  1718. RestartScan);
  1719. RestartScan = FALSE;
  1720. //
  1721. // If there are no files that match the format, then we'll get
  1722. // STATUS_NO_SUCH_FILE.
  1723. //
  1724. if (Status == STATUS_NO_SUCH_FILE && (FileCount == 0)) {
  1725. //
  1726. // We'll return the fact that there are no such files in the
  1727. // directory.
  1728. //
  1729. break;
  1730. }
  1731. if (Status == STATUS_NO_MORE_FILES) {
  1732. //
  1733. // We are done.
  1734. //
  1735. break;
  1736. }
  1737. if (NT_ERROR(Status)) {
  1738. ErrorCode = RtlNtStatusToDosError(Status);
  1739. goto cleanup;
  1740. }
  1741. //
  1742. // Go through the files returned in the buffer.
  1743. //
  1744. for (FileInfo = QueryBuffer;
  1745. ((PUCHAR) FileInfo < ((PUCHAR) QueryBuffer + QueryBufferSize));
  1746. FileInfo = (PVOID) (((PUCHAR) FileInfo) + FileInfo->NextEntryOffset)) {
  1747. FileCount++;
  1748. if (!FileInfo->NextEntryOffset) {
  1749. break;
  1750. }
  1751. }
  1752. }
  1753. *NumFiles = FileCount;
  1754. ErrorCode = ERROR_SUCCESS;
  1755. cleanup:
  1756. DBGPR((PFID,PFTRC,"PFSVC: CountFilesInDirectory(%ws)=%d,%x\n", DirectoryPath, *NumFiles, ErrorCode));
  1757. if (AllocatedDirectoryPathU) {
  1758. RtlFreeHeap(RtlProcessHeap(), 0, DirectoryPathU.Buffer);
  1759. }
  1760. if (OpenedDirectory) {
  1761. NtClose(DirectoryHandle);
  1762. }
  1763. if (QueryBuffer) {
  1764. PFSVC_FREE(QueryBuffer);
  1765. }
  1766. return ErrorCode;
  1767. }
  1768. //
  1769. // Routines to process acquired traces:
  1770. //
  1771. DWORD
  1772. PfSvProcessTrace(
  1773. PPF_TRACE_HEADER Trace
  1774. )
  1775. /*++
  1776. Routine Description:
  1777. This routine is called to process a trace and update the the
  1778. scenario file.
  1779. Arguments:
  1780. Trace - Pointer to trace.
  1781. Return Value:
  1782. Win32 error code.
  1783. --*/
  1784. {
  1785. PPF_SCENARIO_HEADER Scenario;
  1786. PFSVC_SCENARIO_INFO ScenarioInfo;
  1787. WCHAR ScenarioFilePath[MAX_PATH];
  1788. ULONG ScenarioFilePathMaxChars;
  1789. DWORD ErrorCode;
  1790. //
  1791. // Initialize locals.
  1792. //
  1793. PfSvInitializeScenarioInfo(&ScenarioInfo,
  1794. &Trace->ScenarioId,
  1795. Trace->ScenarioType);
  1796. ScenarioFilePathMaxChars = sizeof(ScenarioFilePath) /
  1797. sizeof(ScenarioFilePath[0]);
  1798. Scenario = NULL;
  1799. DBGPR((PFID,PFTRC,"PFSVC: ProcessTrace(%p)\n", Trace));
  1800. //
  1801. // Build file path to existing information for this scenario.
  1802. //
  1803. ErrorCode = PfSvScenarioGetFilePath(ScenarioFilePath,
  1804. ScenarioFilePathMaxChars,
  1805. &Trace->ScenarioId);
  1806. if (ErrorCode != ERROR_SUCCESS) {
  1807. //
  1808. // The buffer we specified should have been big enough. This call
  1809. // should not fail.
  1810. //
  1811. PFSVC_ASSERT(ErrorCode == ERROR_SUCCESS);
  1812. goto cleanup;
  1813. }
  1814. //
  1815. // Map and verify scenario file if it exists. If we cannot open it,
  1816. // NULL Scenario should be returned.
  1817. //
  1818. ErrorCode = PfSvScenarioOpen(ScenarioFilePath,
  1819. &Trace->ScenarioId,
  1820. Trace->ScenarioType,
  1821. &Scenario);
  1822. PFSVC_ASSERT(Scenario || ErrorCode);
  1823. //
  1824. // Allocate memory upfront for trace & scenario processing.
  1825. //
  1826. ErrorCode = PfSvScenarioInfoPreallocate(&ScenarioInfo,
  1827. Scenario,
  1828. Trace);
  1829. if (ErrorCode != ERROR_SUCCESS) {
  1830. goto cleanup;
  1831. }
  1832. //
  1833. // Incorporate information from any existing scenario file.
  1834. //
  1835. if (Scenario) {
  1836. ErrorCode = PfSvAddExistingScenarioInfo(&ScenarioInfo, Scenario);
  1837. if (ErrorCode != ERROR_SUCCESS) {
  1838. goto cleanup;
  1839. }
  1840. //
  1841. // Unmap the scenario so we can write over it when done.
  1842. //
  1843. UnmapViewOfFile(Scenario);
  1844. Scenario = NULL;
  1845. }
  1846. //
  1847. // If this is the first launch of this scenario, it is likely that we
  1848. // will create a new scenario file for it.
  1849. //
  1850. if (ScenarioInfo.ScenHeader.NumLaunches == 1) {
  1851. //
  1852. // Do we already have too many scenario files in the prefetch directory?
  1853. //
  1854. if (PfSvcGlobals.NumPrefetchFiles > PFSVC_MAX_PREFETCH_FILES) {
  1855. //
  1856. // If this is not the boot scenario, we'll ignore it. We don't
  1857. // create new scenario files until we clean up the old ones.
  1858. //
  1859. if (ScenarioInfo.ScenHeader.ScenarioType != PfSystemBootScenarioType) {
  1860. #ifndef PFSVC_DBG
  1861. ErrorCode = ERROR_TOO_MANY_OPEN_FILES;
  1862. goto cleanup;
  1863. #endif // !PFSVC_DBG
  1864. }
  1865. }
  1866. PfSvcGlobals.NumPrefetchFiles++;
  1867. }
  1868. //
  1869. // Verify that volume magics from existing scenario match those in
  1870. // the new trace. If volumes change beneath us we'd need to fix
  1871. // file paths in the existing scenario. But that is too much work,
  1872. // so for now we just start new.
  1873. //
  1874. if (!PfSvVerifyVolumeMagics(&ScenarioInfo, Trace)) {
  1875. PfSvCleanupScenarioInfo(&ScenarioInfo);
  1876. PfSvInitializeScenarioInfo(&ScenarioInfo,
  1877. &Trace->ScenarioId,
  1878. Trace->ScenarioType);
  1879. ErrorCode = PfSvScenarioInfoPreallocate(&ScenarioInfo,
  1880. NULL,
  1881. Trace);
  1882. if (ErrorCode != ERROR_SUCCESS) {
  1883. goto cleanup;
  1884. }
  1885. //
  1886. // Also delete the existing scenario instruction in case we
  1887. // fail to update them since they are invalid now.
  1888. //
  1889. PfSvcGlobals.NumPrefetchFiles--;
  1890. DeleteFile(ScenarioFilePath);
  1891. }
  1892. //
  1893. // Merge information from new trace.
  1894. //
  1895. ErrorCode = PfSvAddTraceInfo(&ScenarioInfo, Trace);
  1896. if (ErrorCode != ERROR_SUCCESS) {
  1897. goto cleanup;
  1898. }
  1899. //
  1900. // Decide which pages to actually prefetch next time, and
  1901. // eliminate uninteresting sections and pages.
  1902. //
  1903. ErrorCode = PfSvApplyPrefetchPolicy(&ScenarioInfo);
  1904. if (ErrorCode != ERROR_SUCCESS) {
  1905. goto cleanup;
  1906. }
  1907. //
  1908. // If no pages/sections are left in the scenario after applying
  1909. // the policy, we'll delete the scenario file.
  1910. //
  1911. if (ScenarioInfo.ScenHeader.NumSections == 0 ||
  1912. ScenarioInfo.ScenHeader.NumPages == 0) {
  1913. //
  1914. // We cannot have sections without pages or vice versa.
  1915. //
  1916. PFSVC_ASSERT(ScenarioInfo.ScenHeader.NumSections == 0);
  1917. PFSVC_ASSERT(ScenarioInfo.ScenHeader.NumPages == 0);
  1918. //
  1919. // Remove the scenario file.
  1920. //
  1921. DeleteFile(ScenarioFilePath);
  1922. ErrorCode = ERROR_BAD_FORMAT;
  1923. goto cleanup;
  1924. }
  1925. //
  1926. // Sort remaining sections by first access.
  1927. //
  1928. ErrorCode = PfSvSortSectionNodesByFirstAccess(&ScenarioInfo.SectionList);
  1929. if (ErrorCode != ERROR_SUCCESS) {
  1930. goto cleanup;
  1931. }
  1932. //
  1933. // Write out new scenario file.
  1934. //
  1935. ErrorCode = PfSvWriteScenario(&ScenarioInfo, ScenarioFilePath);
  1936. //
  1937. // Fall through with status.
  1938. //
  1939. cleanup:
  1940. PfSvCleanupScenarioInfo(&ScenarioInfo);
  1941. if (Scenario) {
  1942. UnmapViewOfFile(Scenario);
  1943. }
  1944. DBGPR((PFID,PFTRC,"PFSVC: ProcessTrace(%p)=%x\n", Trace, ErrorCode));
  1945. return ErrorCode;
  1946. }
  1947. VOID
  1948. PfSvInitializeScenarioInfo (
  1949. PPFSVC_SCENARIO_INFO ScenarioInfo,
  1950. PPF_SCENARIO_ID ScenarioId,
  1951. PF_SCENARIO_TYPE ScenarioType
  1952. )
  1953. /*++
  1954. Routine Description:
  1955. This routine initializes the specified new scenario structure. It
  1956. sets the fields of the embedded scenario header as if no previous
  1957. scenario information is available.
  1958. Arguments:
  1959. ScenarioInfo - Pointer to structure to initialize.
  1960. ScenarioId & ScenarioType - Identifiers for the scenario.
  1961. Return Value:
  1962. None.
  1963. --*/
  1964. {
  1965. //
  1966. // Initialize ScenarioInfo so we know what to cleanup. Zeroing the structure
  1967. // takes care of the following fields:
  1968. // OneBigAllocation
  1969. // NewPages
  1970. // HitPages
  1971. // MissedOpportunityPages
  1972. // IgnoredPages
  1973. // PrefetchedPages
  1974. //
  1975. RtlZeroMemory(ScenarioInfo, sizeof(PFSVC_SCENARIO_INFO));
  1976. InitializeListHead(&ScenarioInfo->SectionList);
  1977. InitializeListHead(&ScenarioInfo->VolumeList);
  1978. PfSvChunkAllocatorInitialize(&ScenarioInfo->SectionNodeAllocator);
  1979. PfSvChunkAllocatorInitialize(&ScenarioInfo->PageNodeAllocator);
  1980. PfSvChunkAllocatorInitialize(&ScenarioInfo->VolumeNodeAllocator);
  1981. PfSvStringAllocatorInitialize(&ScenarioInfo->PathAllocator);
  1982. //
  1983. // Initialize the embedded scenario header.
  1984. //
  1985. ScenarioInfo->ScenHeader.Version = PF_CURRENT_VERSION;
  1986. ScenarioInfo->ScenHeader.MagicNumber = PF_SCENARIO_MAGIC_NUMBER;
  1987. ScenarioInfo->ScenHeader.ServiceVersion = PFSVC_SERVICE_VERSION;
  1988. ScenarioInfo->ScenHeader.Size = 0;
  1989. ScenarioInfo->ScenHeader.ScenarioId = *ScenarioId;
  1990. ScenarioInfo->ScenHeader.ScenarioType = ScenarioType;
  1991. ScenarioInfo->ScenHeader.NumSections = 0;
  1992. ScenarioInfo->ScenHeader.NumPages = 0;
  1993. ScenarioInfo->ScenHeader.FileNameInfoSize = 0;
  1994. ScenarioInfo->ScenHeader.NumLaunches = 1;
  1995. ScenarioInfo->ScenHeader.Sensitivity = PF_MIN_SENSITIVITY;
  1996. //
  1997. // These fields help us not prefetch if a scenario is getting
  1998. // launched too frequently. RePrefetchTime and ReTraceTime's get
  1999. // set to default values after the scenario is launched a number
  2000. // of times. This allows training scenarios run after clearing the
  2001. // prefetch cache to be traced correctly.
  2002. //
  2003. ScenarioInfo->ScenHeader.LastLaunchTime.QuadPart = 0;
  2004. ScenarioInfo->ScenHeader.MinRePrefetchTime.QuadPart = 0;
  2005. ScenarioInfo->ScenHeader.MinReTraceTime.QuadPart = 0;
  2006. return;
  2007. }
  2008. VOID
  2009. PfSvCleanupScenarioInfo(
  2010. PPFSVC_SCENARIO_INFO ScenarioInfo
  2011. )
  2012. /*++
  2013. Routine Description:
  2014. This function cleans up a scenario info structure. It does not
  2015. free the structure itself. The structure should have been
  2016. initialized by PfSvInitializeScenarioInfo.
  2017. Arguments:
  2018. ScenarioInfo - Pointer to structure.
  2019. Return Value:
  2020. None.
  2021. --*/
  2022. {
  2023. PPFSVC_SECTION_NODE SectionNode;
  2024. PLIST_ENTRY SectListEntry;
  2025. PPFSVC_VOLUME_NODE VolumeNode;
  2026. PLIST_ENTRY VolumeListEntry;
  2027. //
  2028. // Walk through the volume nodes and free them. Do this before
  2029. // freeing section nodes, so when we are trying to cleanup a
  2030. // section node, it is not on a volume node's list.
  2031. //
  2032. while (!IsListEmpty(&ScenarioInfo->VolumeList)) {
  2033. VolumeListEntry = RemoveHeadList(&ScenarioInfo->VolumeList);
  2034. VolumeNode = CONTAINING_RECORD(VolumeListEntry,
  2035. PFSVC_VOLUME_NODE,
  2036. VolumeLink);
  2037. //
  2038. // Cleanup the volume node.
  2039. //
  2040. PfSvCleanupVolumeNode(ScenarioInfo, VolumeNode);
  2041. //
  2042. // Free the volume node.
  2043. //
  2044. PfSvChunkAllocatorFree(&ScenarioInfo->VolumeNodeAllocator, VolumeNode);
  2045. }
  2046. //
  2047. // Walk through the section nodes and free them.
  2048. //
  2049. while (!IsListEmpty(&ScenarioInfo->SectionList)) {
  2050. SectListEntry = RemoveHeadList(&ScenarioInfo->SectionList);
  2051. SectionNode = CONTAINING_RECORD(SectListEntry,
  2052. PFSVC_SECTION_NODE,
  2053. SectionLink);
  2054. //
  2055. // Cleanup the section node.
  2056. //
  2057. PfSvCleanupSectionNode(ScenarioInfo, SectionNode);
  2058. //
  2059. // Free the section node.
  2060. //
  2061. PfSvChunkAllocatorFree(&ScenarioInfo->SectionNodeAllocator, SectionNode);
  2062. }
  2063. //
  2064. // Cleanup allocators.
  2065. //
  2066. PfSvChunkAllocatorCleanup(&ScenarioInfo->SectionNodeAllocator);
  2067. PfSvChunkAllocatorCleanup(&ScenarioInfo->PageNodeAllocator);
  2068. PfSvChunkAllocatorCleanup(&ScenarioInfo->VolumeNodeAllocator);
  2069. PfSvStringAllocatorCleanup(&ScenarioInfo->PathAllocator);
  2070. //
  2071. // Free the one big allocation we made.
  2072. //
  2073. if (ScenarioInfo->OneBigAllocation) {
  2074. PFSVC_FREE(ScenarioInfo->OneBigAllocation);
  2075. }
  2076. return;
  2077. }
  2078. DWORD
  2079. PfSvScenarioGetFilePath(
  2080. OUT PWCHAR FilePath,
  2081. IN ULONG FilePathMaxChars,
  2082. IN PPF_SCENARIO_ID ScenarioId
  2083. )
  2084. /*++
  2085. Routine Description:
  2086. This routine builds the file path for the specified scenario.
  2087. Arguments:
  2088. FilePath - Output buffer.
  2089. FilePathMaxChars - Size of FilePath buffer in characters including NUL.
  2090. ScenarioId - Scenario identifier.
  2091. Return Value:
  2092. Win32 error code.
  2093. --*/
  2094. {
  2095. ULONG NumChars;
  2096. DWORD ErrorCode;
  2097. WCHAR ScenarioFileName[PF_MAX_SCENARIO_FILE_NAME];
  2098. BOOLEAN AcquiredPrefetchRootLock;
  2099. //
  2100. // Get the lock so the path to prefetch folder does not change
  2101. // beneath our feet.
  2102. //
  2103. PFSVC_ACQUIRE_LOCK(PfSvcGlobals.PrefetchRootLock);
  2104. AcquiredPrefetchRootLock = TRUE;
  2105. //
  2106. // Calculate how big an input buffer we will need.
  2107. //
  2108. NumChars = wcslen(PfSvcGlobals.PrefetchRoot);
  2109. NumChars += wcslen(L"\\");
  2110. NumChars += PF_MAX_SCENARIO_FILE_NAME;
  2111. if (NumChars >= FilePathMaxChars) {
  2112. ErrorCode = ERROR_INSUFFICIENT_BUFFER;
  2113. goto cleanup;
  2114. }
  2115. //
  2116. // Build the scenario file name from scenario identifier.
  2117. //
  2118. swprintf(ScenarioFileName,
  2119. PF_SCEN_FILE_NAME_FORMAT,
  2120. ScenarioId->ScenName,
  2121. ScenarioId->HashId,
  2122. PF_PREFETCH_FILE_EXTENSION);
  2123. //
  2124. // Build file path from prefetch directory path and file name.
  2125. //
  2126. swprintf(FilePath,
  2127. L"%ws\\%ws",
  2128. PfSvcGlobals.PrefetchRoot,
  2129. ScenarioFileName);
  2130. PFSVC_ASSERT(wcslen(FilePath) < FilePathMaxChars);
  2131. PFSVC_RELEASE_LOCK(PfSvcGlobals.PrefetchRootLock);
  2132. AcquiredPrefetchRootLock = FALSE;
  2133. ErrorCode = ERROR_SUCCESS;
  2134. cleanup:
  2135. if (AcquiredPrefetchRootLock) {
  2136. PFSVC_RELEASE_LOCK(PfSvcGlobals.PrefetchRootLock);
  2137. }
  2138. return ErrorCode;
  2139. }
  2140. DWORD
  2141. PfSvScenarioOpen (
  2142. IN PWCHAR FilePath,
  2143. IN PPF_SCENARIO_ID ScenarioId,
  2144. IN PF_SCENARIO_TYPE ScenarioType,
  2145. OUT PPF_SCENARIO_HEADER *Scenario
  2146. )
  2147. /*++
  2148. Routine Description:
  2149. This routine maps & verifies the scenario instructions at FilePath.
  2150. If a Scenario is returned, caller has to call UnmapViewOfFile to cleanup.
  2151. Arguments:
  2152. FilePath - Path to scenario instructions.
  2153. Scenario - Pointer to base of mapping of scenario instructions or NULL
  2154. if the function returns an error.
  2155. Return Value:
  2156. Win32 error code.
  2157. --*/
  2158. {
  2159. PPF_SCENARIO_HEADER OpenedScenario;
  2160. DWORD FailedCheck;
  2161. DWORD ErrorCode;
  2162. DWORD FileSize;
  2163. //
  2164. // Initialize locals.
  2165. //
  2166. OpenedScenario = NULL;
  2167. //
  2168. // Initialize output parameters.
  2169. //
  2170. *Scenario = NULL;
  2171. //
  2172. // Try to map the scenario file.
  2173. //
  2174. ErrorCode = PfSvGetViewOfFile(FilePath,
  2175. &OpenedScenario,
  2176. &FileSize);
  2177. if (ErrorCode != ERROR_SUCCESS) {
  2178. goto cleanup;
  2179. }
  2180. //
  2181. // Verify the scenario file.
  2182. //
  2183. FailedCheck = 0;
  2184. if (!PfSvVerifyScenarioBuffer(OpenedScenario, FileSize, &FailedCheck) ||
  2185. (OpenedScenario->ScenarioType != ScenarioType) ||
  2186. OpenedScenario->ServiceVersion != PFSVC_SERVICE_VERSION) {
  2187. //
  2188. // This is a bogus / wrong / outdated scenario file. Remove
  2189. // it.
  2190. //
  2191. UnmapViewOfFile(OpenedScenario);
  2192. OpenedScenario = NULL;
  2193. DeleteFile(FilePath);
  2194. ErrorCode = ERROR_BAD_FORMAT;
  2195. goto cleanup;
  2196. }
  2197. *Scenario = OpenedScenario;
  2198. ErrorCode = ERROR_SUCCESS;
  2199. cleanup:
  2200. if (ErrorCode != ERROR_SUCCESS) {
  2201. if (OpenedScenario) {
  2202. UnmapViewOfFile(OpenedScenario);
  2203. }
  2204. *Scenario = NULL;
  2205. } else {
  2206. //
  2207. // If we are returning success we should be returning a valid Scenario.
  2208. //
  2209. PFSVC_ASSERT(*Scenario);
  2210. }
  2211. return ErrorCode;
  2212. }
  2213. DWORD
  2214. PfSvScenarioInfoPreallocate(
  2215. IN PPFSVC_SCENARIO_INFO ScenarioInfo,
  2216. OPTIONAL IN PPF_SCENARIO_HEADER Scenario,
  2217. OPTIONAL IN PPF_TRACE_HEADER Trace
  2218. )
  2219. /*++
  2220. Routine Description:
  2221. This routine preallocates a heap to be divided up and used by the
  2222. various allocators when processing a prefetch trace. The default allocation
  2223. size is determined from the Trace and Scenario size.
  2224. Arguments:
  2225. ScenarioInfo - Pointer to scenario containing allocators to initialize.
  2226. Scenario - Pointer to scenario instructions.
  2227. Trace - Pointer to prefetch trace.
  2228. Return Value:
  2229. Win32 error code.
  2230. --*/
  2231. {
  2232. PUCHAR Allocation;
  2233. PUCHAR ChunkStart;
  2234. DWORD ErrorCode;
  2235. ULONG AllocationSize;
  2236. ULONG NumSections;
  2237. ULONG NumPages;
  2238. ULONG NumVolumes;
  2239. ULONG PathSize;
  2240. //
  2241. // Initialize locals.
  2242. //
  2243. Allocation = NULL;
  2244. NumSections = 0;
  2245. NumPages = 0;
  2246. NumVolumes = 0;
  2247. PathSize = 0;
  2248. //
  2249. // Estimate how much to preallocate. Over-estimate rather than under-
  2250. // estimate because we will have to go to the heap for individual allocations
  2251. // if we underestimate. If we overestimate, as long as we don't touch the extra
  2252. // pages allocated we don't get a hit.
  2253. //
  2254. if (Trace) {
  2255. NumSections += Trace->NumSections;
  2256. }
  2257. if (Scenario) {
  2258. NumSections += Scenario->NumSections;
  2259. }
  2260. if (Trace) {
  2261. NumPages += Trace->NumEntries;
  2262. }
  2263. if (Scenario) {
  2264. NumPages += Scenario->NumPages;
  2265. }
  2266. if (Trace) {
  2267. NumVolumes += Trace->NumVolumes;
  2268. }
  2269. if (Scenario) {
  2270. NumVolumes += Scenario->NumMetadataRecords;
  2271. //
  2272. // It is very likely that we will at least share the volume containing the
  2273. // main executables between the trace and existing scenario instructions.
  2274. // So if we have both Trace and Scenario take one volume node off the estimate.
  2275. //
  2276. if (Trace) {
  2277. PFSVC_ASSERT(NumVolumes);
  2278. NumVolumes--;
  2279. }
  2280. }
  2281. //
  2282. // It is hard to estimate how much we will allocate for various paths
  2283. // e.g. file paths & each level of parent directory paths etc. It should be less
  2284. // than the size of the total trace, although it probably makes up most of it.
  2285. //
  2286. if (Trace) {
  2287. PathSize += Trace->Size;
  2288. }
  2289. if (Scenario) {
  2290. PathSize += Scenario->FileNameInfoSize;
  2291. PathSize += Scenario->MetadataInfoSize;
  2292. }
  2293. //
  2294. // Add it all up.
  2295. //
  2296. AllocationSize = 0;
  2297. AllocationSize += _alignof(PFSVC_VOLUME_NODE);
  2298. AllocationSize += NumVolumes * sizeof(PFSVC_VOLUME_NODE);
  2299. AllocationSize += _alignof(PFSVC_SECTION_NODE);
  2300. AllocationSize += NumSections * sizeof(PFSVC_SECTION_NODE);
  2301. AllocationSize += _alignof(PFSVC_PAGE_NODE);
  2302. AllocationSize += NumPages * sizeof(PFSVC_PAGE_NODE);
  2303. AllocationSize += PathSize;
  2304. //
  2305. // Make one big allocation.
  2306. //
  2307. Allocation = PFSVC_ALLOC(AllocationSize);
  2308. if (!Allocation) {
  2309. ErrorCode = ERROR_NOT_ENOUGH_MEMORY;
  2310. goto cleanup;
  2311. }
  2312. //
  2313. // Divide up the big allocation. Since we are providing the buffers,
  2314. // allocators should not fail.
  2315. //
  2316. ChunkStart = Allocation;
  2317. //
  2318. // Volume nodes.
  2319. //
  2320. ErrorCode = PfSvChunkAllocatorStart(&ScenarioInfo->VolumeNodeAllocator,
  2321. ChunkStart,
  2322. sizeof(PFSVC_VOLUME_NODE),
  2323. NumVolumes);
  2324. if (ErrorCode != ERROR_SUCCESS) {
  2325. PFSVC_ASSERT(ErrorCode == ERROR_SUCCESS);
  2326. goto cleanup;
  2327. }
  2328. ChunkStart += (ULONG_PTR) NumVolumes * sizeof(PFSVC_VOLUME_NODE);
  2329. //
  2330. // Section nodes.
  2331. //
  2332. ChunkStart = PF_ALIGN_UP(ChunkStart, _alignof(PFSVC_SECTION_NODE));
  2333. ErrorCode = PfSvChunkAllocatorStart(&ScenarioInfo->SectionNodeAllocator,
  2334. ChunkStart,
  2335. sizeof(PFSVC_SECTION_NODE),
  2336. NumSections);
  2337. if (ErrorCode != ERROR_SUCCESS) {
  2338. PFSVC_ASSERT(ErrorCode == ERROR_SUCCESS);
  2339. goto cleanup;
  2340. }
  2341. ChunkStart += (ULONG_PTR) NumSections * sizeof(PFSVC_SECTION_NODE);
  2342. //
  2343. // Page nodes.
  2344. //
  2345. ChunkStart = PF_ALIGN_UP(ChunkStart, _alignof(PFSVC_PAGE_NODE));
  2346. ErrorCode = PfSvChunkAllocatorStart(&ScenarioInfo->PageNodeAllocator,
  2347. ChunkStart,
  2348. sizeof(PFSVC_PAGE_NODE),
  2349. NumPages);
  2350. if (ErrorCode != ERROR_SUCCESS) {
  2351. PFSVC_ASSERT(ErrorCode == ERROR_SUCCESS);
  2352. goto cleanup;
  2353. }
  2354. ChunkStart += (ULONG_PTR) NumPages * sizeof(PFSVC_PAGE_NODE);
  2355. //
  2356. // Path names.
  2357. //
  2358. ErrorCode = PfSvStringAllocatorStart(&ScenarioInfo->PathAllocator,
  2359. ChunkStart,
  2360. PathSize);
  2361. if (ErrorCode != ERROR_SUCCESS) {
  2362. PFSVC_ASSERT(ErrorCode == ERROR_SUCCESS);
  2363. goto cleanup;
  2364. }
  2365. ChunkStart += (ULONG_PTR) PathSize;
  2366. //
  2367. // We should not have passed beyond what we allocated.
  2368. //
  2369. PFSVC_ASSERT(ChunkStart > (PUCHAR) Allocation);
  2370. PFSVC_ASSERT(ChunkStart < (PUCHAR) Allocation + (ULONG_PTR) AllocationSize);
  2371. ScenarioInfo->OneBigAllocation = Allocation;
  2372. ErrorCode = ERROR_SUCCESS;
  2373. cleanup:
  2374. if (ErrorCode != ERROR_SUCCESS) {
  2375. if (Allocation) {
  2376. PFSVC_FREE(Allocation);
  2377. }
  2378. }
  2379. return ErrorCode;
  2380. }
  2381. DWORD
  2382. PfSvAddExistingScenarioInfo(
  2383. PPFSVC_SCENARIO_INFO ScenarioInfo,
  2384. PPF_SCENARIO_HEADER Scenario
  2385. )
  2386. /*++
  2387. Routine Description:
  2388. This function gets existing scenario information for the specified
  2389. scenario and updates ScenarioInfo.
  2390. Arguments:
  2391. ScenarioInfo - Initialized scenario info structure.
  2392. Scenario - Pointer to mapped scenario instructions.
  2393. Return Value:
  2394. Win32 error code.
  2395. --*/
  2396. {
  2397. DWORD ErrorCode;
  2398. PPFSVC_SECTION_NODE SectionNode;
  2399. PPFSVC_PAGE_NODE PageNode;
  2400. PPF_SECTION_RECORD Sections;
  2401. PPF_SECTION_RECORD SectionRecord;
  2402. PPF_PAGE_RECORD Pages;
  2403. PCHAR FileNameInfo;
  2404. WCHAR *FileName;
  2405. LONG PageIdx;
  2406. ULONG SectionIdx;
  2407. ULONG NumPages;
  2408. PCHAR MetadataInfoBase;
  2409. PPF_METADATA_RECORD MetadataRecordTable;
  2410. PPF_METADATA_RECORD MetadataRecord;
  2411. ULONG MetadataRecordIdx;
  2412. PWCHAR VolumePath;
  2413. //
  2414. // Copy over the existing scenario header.
  2415. //
  2416. ScenarioInfo->ScenHeader = *Scenario;
  2417. //
  2418. // Update number of launches.
  2419. //
  2420. ScenarioInfo->ScenHeader.NumLaunches++;
  2421. //
  2422. // Convert the scenario data into intermediate data structures
  2423. // we can manipulate easier:
  2424. //
  2425. //
  2426. // Create volume nodes from metadata records.
  2427. //
  2428. MetadataInfoBase = (PCHAR)Scenario + Scenario->MetadataInfoOffset;
  2429. MetadataRecordTable = (PPF_METADATA_RECORD) MetadataInfoBase;
  2430. for (MetadataRecordIdx = 0;
  2431. MetadataRecordIdx < Scenario->NumMetadataRecords;
  2432. MetadataRecordIdx++) {
  2433. MetadataRecord = &MetadataRecordTable[MetadataRecordIdx];
  2434. VolumePath = (PWCHAR)(MetadataInfoBase + MetadataRecord->VolumeNameOffset);
  2435. ErrorCode = PfSvCreateVolumeNode(ScenarioInfo,
  2436. VolumePath,
  2437. MetadataRecord->VolumeNameLength,
  2438. &MetadataRecord->CreationTime,
  2439. MetadataRecord->SerialNumber);
  2440. if (ErrorCode != ERROR_SUCCESS) {
  2441. goto cleanup;
  2442. }
  2443. }
  2444. //
  2445. // Convert page and section nodes.
  2446. //
  2447. Sections = (PPF_SECTION_RECORD) ((PCHAR)Scenario + Scenario->SectionInfoOffset);
  2448. Pages = (PPF_PAGE_RECORD) ((PCHAR)Scenario + Scenario->PageInfoOffset);
  2449. FileNameInfo = (PCHAR)Scenario + Scenario->FileNameInfoOffset;
  2450. for (SectionIdx = 0; SectionIdx < Scenario->NumSections; SectionIdx++) {
  2451. //
  2452. // Build a section node from this section record in the
  2453. // scenario file. PfSvGetSectionRecord will insert it into
  2454. // the new scenario by the section record's name.
  2455. //
  2456. SectionRecord = &Sections[SectionIdx];
  2457. FileName = (PWSTR) (FileNameInfo + SectionRecord->FileNameOffset);
  2458. SectionNode = PfSvGetSectionRecord (ScenarioInfo,
  2459. FileName,
  2460. SectionRecord->FileNameLength);
  2461. if (!SectionNode) {
  2462. ErrorCode = ERROR_NOT_ENOUGH_MEMORY;
  2463. goto cleanup;
  2464. }
  2465. //
  2466. // There should not be duplicate sections in the
  2467. // scenario. The section node we got should have an empty
  2468. // section record.
  2469. //
  2470. PFSVC_ASSERT(SectionNode->SectionRecord.FirstPageIdx == 0);
  2471. PFSVC_ASSERT(SectionNode->SectionRecord.NumPages == 0);
  2472. PFSVC_ASSERT(SectionNode->OrgSectionIndex == ULONG_MAX);
  2473. //
  2474. // Update the index of this section in the scenario file.
  2475. //
  2476. SectionNode->OrgSectionIndex = SectionIdx;
  2477. //
  2478. // Update the section record in the section node.
  2479. //
  2480. SectionNode->SectionRecord = *SectionRecord;
  2481. //
  2482. // Put page records for the section into the list.
  2483. //
  2484. PageIdx = SectionRecord->FirstPageIdx;
  2485. NumPages = 0;
  2486. while (PageIdx != PF_INVALID_PAGE_IDX) {
  2487. if (NumPages >= SectionRecord->NumPages) {
  2488. //
  2489. // There should not be more pages on the list than
  2490. // what the section record says there is.
  2491. //
  2492. PFSVC_ASSERT(FALSE);
  2493. break;
  2494. }
  2495. PageNode = PfSvChunkAllocatorAllocate(&ScenarioInfo->PageNodeAllocator);
  2496. if (!PageNode) {
  2497. ErrorCode = ERROR_NOT_ENOUGH_MEMORY;
  2498. goto cleanup;
  2499. }
  2500. //
  2501. // Copy over the page record.
  2502. //
  2503. PageNode->PageRecord = Pages[PageIdx];
  2504. //
  2505. // Insert it into the section's page list. Note that
  2506. // the page records in the section should be sorted by
  2507. // offset. By inserting to the tail, we maintain that.
  2508. //
  2509. InsertTailList(&SectionNode->PageList, &PageNode->PageLink);
  2510. //
  2511. // Shift the usage history for this page record making
  2512. // room for whether this page was used in this launch.
  2513. //
  2514. PageNode->PageRecord.UsageHistory <<= 1;
  2515. //
  2516. // Shift the prefetch history for this page record and
  2517. // note whether we had asked this page to be
  2518. // prefetched in this launch.
  2519. //
  2520. PageNode->PageRecord.PrefetchHistory <<= 1;
  2521. if (!PageNode->PageRecord.IsIgnore) {
  2522. PageNode->PageRecord.PrefetchHistory |= 0x1;
  2523. }
  2524. //
  2525. // Keep the count of pages we had asked to be
  2526. // prefetched, so we can calculate hit rate and adjust
  2527. // the sensitivity.
  2528. //
  2529. if(!PageNode->PageRecord.IsIgnore) {
  2530. if (PageNode->PageRecord.IsImage) {
  2531. ScenarioInfo->PrefetchedPages++;
  2532. }
  2533. if (PageNode->PageRecord.IsData) {
  2534. ScenarioInfo->PrefetchedPages++;
  2535. }
  2536. } else {
  2537. ScenarioInfo->IgnoredPages++;
  2538. }
  2539. //
  2540. // Update next page idx.
  2541. //
  2542. PageIdx = Pages[PageIdx].NextPageIdx;
  2543. //
  2544. // Update number of pages we've copied.
  2545. //
  2546. NumPages++;
  2547. }
  2548. //
  2549. // We should have copied as many pages as the section said
  2550. // there were.
  2551. //
  2552. PFSVC_ASSERT(NumPages == SectionRecord->NumPages);
  2553. }
  2554. ErrorCode = ERROR_SUCCESS;
  2555. cleanup:
  2556. return ErrorCode;
  2557. }
  2558. DWORD
  2559. PfSvVerifyVolumeMagics(
  2560. PPFSVC_SCENARIO_INFO ScenarioInfo,
  2561. PPF_TRACE_HEADER Trace
  2562. )
  2563. /*++
  2564. Routine Description:
  2565. Walk through the volumes in the trace and make sure their magics
  2566. match the ones in ScenarioInfo.
  2567. Arguments:
  2568. ScenarioInfo - Pointer to scenario info structure.
  2569. Trace - Pointer to trace.
  2570. Return Value:
  2571. Win32 error code.
  2572. --*/
  2573. {
  2574. PPFSVC_VOLUME_NODE VolumeNode;
  2575. PPF_VOLUME_INFO VolumeInfo;
  2576. ULONG VolumeInfoSize;
  2577. ULONG VolumeIdx;
  2578. BOOLEAN VolumeMagicsMatch;
  2579. //
  2580. // Walk the volumes in the trace.
  2581. //
  2582. VolumeInfo = (PPF_VOLUME_INFO) ((PCHAR)Trace + Trace->VolumeInfoOffset);
  2583. for (VolumeIdx = 0; VolumeIdx < Trace->NumVolumes; VolumeIdx++) {
  2584. //
  2585. // Get the scenario info's volume node for this volume.
  2586. //
  2587. VolumeNode = PfSvGetVolumeNode(ScenarioInfo,
  2588. VolumeInfo->VolumePath,
  2589. VolumeInfo->VolumePathLength);
  2590. if (VolumeNode) {
  2591. //
  2592. // Make sure the magics match.
  2593. //
  2594. if (VolumeNode->SerialNumber != VolumeInfo->SerialNumber ||
  2595. VolumeNode->CreationTime.QuadPart != VolumeInfo->CreationTime.QuadPart) {
  2596. VolumeMagicsMatch = FALSE;
  2597. goto cleanup;
  2598. }
  2599. }
  2600. //
  2601. // Get the next volume.
  2602. //
  2603. VolumeInfoSize = sizeof(PF_VOLUME_INFO);
  2604. VolumeInfoSize += VolumeInfo->VolumePathLength * sizeof(WCHAR);
  2605. VolumeInfo = (PPF_VOLUME_INFO) ((PCHAR) VolumeInfo + VolumeInfoSize);
  2606. //
  2607. // Make sure VolumeInfo is aligned.
  2608. //
  2609. VolumeInfo = PF_ALIGN_UP(VolumeInfo, _alignof(PF_VOLUME_INFO));
  2610. }
  2611. //
  2612. // Volume magics for volumes that appear both in the trace and the
  2613. // scenario info matched.
  2614. //
  2615. VolumeMagicsMatch = TRUE;
  2616. cleanup:
  2617. return VolumeMagicsMatch;
  2618. }
  2619. DWORD
  2620. PfSvAddTraceInfo(
  2621. PPFSVC_SCENARIO_INFO ScenarioInfo,
  2622. PPF_TRACE_HEADER Trace
  2623. )
  2624. /*++
  2625. Routine Description:
  2626. Add information in a raw trace to the specified scenario info
  2627. structure.
  2628. Arguments:
  2629. ScenarioInfo - Pointer to scenario info structure.
  2630. Trace - Pointer to trace.
  2631. Return Value:
  2632. Win32 error code.
  2633. --*/
  2634. {
  2635. PPF_SECTION_INFO Section;
  2636. PPF_LOG_ENTRY LogEntries;
  2637. PCHAR pFileName;
  2638. PPFSVC_SECTION_NODE *SectionTable;
  2639. PPFSVC_SECTION_NODE SectionNode;
  2640. PPFSVC_VOLUME_NODE VolumeNode;
  2641. ULONG TraceEndIdx;
  2642. ULONG SectionIdx;
  2643. ULONG EntryIdx;
  2644. DWORD ErrorCode;
  2645. ULONG SectionLength;
  2646. ULONG NextSectionIndex;
  2647. PPF_VOLUME_INFO VolumeInfo;
  2648. ULONG VolumeInfoSize;
  2649. ULONG VolumeIdx;
  2650. ULONG SectionTableSize;
  2651. //
  2652. // Initialize locals so we know what to clean up.
  2653. //
  2654. SectionTable = NULL;
  2655. DBGPR((PFID,PFTRC,"PFSVC: AddTraceInfo()\n"));
  2656. //
  2657. // Update last launch time.
  2658. //
  2659. ScenarioInfo->ScenHeader.LastLaunchTime = Trace->LaunchTime;
  2660. //
  2661. // If this scenario has been launched a number of times, we update
  2662. // the min reprefetch and retrace times. See comment for
  2663. // PFSVC_MIN_LAUNCHES_FOR_LAUNCH_FREQ_CHECK.
  2664. //
  2665. if (ScenarioInfo->ScenHeader.NumLaunches >= PFSVC_MIN_LAUNCHES_FOR_LAUNCH_FREQ_CHECK) {
  2666. ScenarioInfo->ScenHeader.MinRePrefetchTime.QuadPart = PFSVC_DEFAULT_MIN_REPREFETCH_TIME;
  2667. ScenarioInfo->ScenHeader.MinReTraceTime.QuadPart = PFSVC_DEFAULT_MIN_RETRACE_TIME;
  2668. }
  2669. #ifdef PFSVC_DBG
  2670. //
  2671. // On checked build, always set these to 0, so we do prefetch every
  2672. // scenario launch.
  2673. //
  2674. ScenarioInfo->ScenHeader.MinRePrefetchTime.QuadPart = 0;
  2675. ScenarioInfo->ScenHeader.MinReTraceTime.QuadPart = 0;
  2676. #endif // PFSVC_DBG
  2677. //
  2678. // Walk through the volumes in the trace and create volume nodes
  2679. // for them.
  2680. //
  2681. VolumeInfo = (PPF_VOLUME_INFO) ((PCHAR)Trace + Trace->VolumeInfoOffset);
  2682. for (VolumeIdx = 0; VolumeIdx < Trace->NumVolumes; VolumeIdx++) {
  2683. //
  2684. // Upcase the path so we don't have to do expensive case
  2685. // insensitive comparisons.
  2686. //
  2687. _wcsupr(VolumeInfo->VolumePath);
  2688. ErrorCode = PfSvCreateVolumeNode(ScenarioInfo,
  2689. VolumeInfo->VolumePath,
  2690. VolumeInfo->VolumePathLength,
  2691. &VolumeInfo->CreationTime,
  2692. VolumeInfo->SerialNumber);
  2693. if (ErrorCode != ERROR_SUCCESS) {
  2694. goto cleanup;
  2695. }
  2696. //
  2697. // Get the next volume.
  2698. //
  2699. VolumeInfoSize = sizeof(PF_VOLUME_INFO);
  2700. VolumeInfoSize += VolumeInfo->VolumePathLength * sizeof(WCHAR);
  2701. VolumeInfo = (PPF_VOLUME_INFO) ((PCHAR) VolumeInfo + VolumeInfoSize);
  2702. //
  2703. // Make sure VolumeInfo is aligned.
  2704. //
  2705. VolumeInfo = PF_ALIGN_UP(VolumeInfo, _alignof(PF_VOLUME_INFO));
  2706. }
  2707. //
  2708. // Allocate section node table so we know where to put the logged
  2709. // page faults.
  2710. //
  2711. SectionTableSize = sizeof(PPFSVC_SECTION_NODE) * Trace->NumSections;
  2712. SectionTable = PFSVC_ALLOC(SectionTableSize);
  2713. if (!SectionTable) {
  2714. ErrorCode = ERROR_NOT_ENOUGH_MEMORY;
  2715. goto cleanup;
  2716. }
  2717. RtlZeroMemory(SectionTable, SectionTableSize);
  2718. //
  2719. // Walk through the sections in the trace, and either find
  2720. // existing section records in the new scenario or create new
  2721. // ones.
  2722. //
  2723. Section = (PPF_SECTION_INFO) ((PCHAR)Trace + Trace->SectionInfoOffset);
  2724. for (SectionIdx = 0; SectionIdx < Trace->NumSections; SectionIdx++) {
  2725. //
  2726. // Upcase the path so we don't have to do expensive case
  2727. // insensitive comparisons.
  2728. //
  2729. _wcsupr(Section->FileName);
  2730. //
  2731. // If the section is for metafile, simply add it as a directory
  2732. // to be prefetched. We don't keep track of individual faults,
  2733. // since we can't prefetch only parts of directories.
  2734. //
  2735. if (Section->Metafile) {
  2736. VolumeNode = PfSvGetVolumeNode(ScenarioInfo,
  2737. Section->FileName,
  2738. Section->FileNameLength);
  2739. PFSVC_ASSERT(VolumeNode);
  2740. if (VolumeNode) {
  2741. PfSvAddParentDirectoriesToList(&VolumeNode->DirectoryList,
  2742. VolumeNode->VolumePathLength,
  2743. Section->FileName,
  2744. Section->FileNameLength);
  2745. }
  2746. goto NextSection;
  2747. }
  2748. //
  2749. // Find or create a section record for this section.
  2750. //
  2751. SectionTable[SectionIdx] = PfSvGetSectionRecord(ScenarioInfo,
  2752. Section->FileName,
  2753. Section->FileNameLength);
  2754. //
  2755. // If we could not get a record, it is because we had to
  2756. // create one and we did not have enough memory.
  2757. //
  2758. if (!SectionTable[SectionIdx]) {
  2759. ErrorCode = ERROR_NOT_ENOUGH_MEMORY;
  2760. goto cleanup;
  2761. }
  2762. NextSection:
  2763. //
  2764. // Get the next section record in the trace.
  2765. //
  2766. SectionLength = sizeof(PF_SECTION_INFO) +
  2767. (Section->FileNameLength) * sizeof(WCHAR);
  2768. Section = (PPF_SECTION_INFO) ((PUCHAR) Section + SectionLength);
  2769. }
  2770. //
  2771. // Determine after which log entry the trace ends.
  2772. //
  2773. TraceEndIdx = PfSvGetTraceEndIdx(Trace);
  2774. //
  2775. // If the determined trace end is zero (as is the case for most
  2776. // applications running under stress that don't get any pagefaults
  2777. // traced for the first few seconds), bail out.
  2778. //
  2779. if (TraceEndIdx == 0) {
  2780. ErrorCode = ERROR_BAD_FORMAT;
  2781. goto cleanup;
  2782. }
  2783. //
  2784. // Add logged pagefault information up to the determined trace end
  2785. // to the new scenario info.
  2786. //
  2787. LogEntries = (PPF_LOG_ENTRY) ((PCHAR)Trace + Trace->TraceBufferOffset);
  2788. //
  2789. // Keep track of NextSectionIdx so we can order the sections by
  2790. // the first access [i.e. first page fault in the trace]
  2791. //
  2792. NextSectionIndex = 0;
  2793. for (EntryIdx = 0; EntryIdx < TraceEndIdx; EntryIdx++) {
  2794. SectionNode = SectionTable[LogEntries[EntryIdx].SectionId];
  2795. //
  2796. // For metafile sections we don't create section nodes.
  2797. //
  2798. if (!SectionNode) {
  2799. continue;
  2800. }
  2801. //
  2802. // NewSectionIndex fields of all section nodes are initialized
  2803. // to ULONG_MAX. If we have not already seen this section in
  2804. // the trace note its order and update NextSectionIdx.
  2805. //
  2806. if (SectionNode->NewSectionIndex == ULONG_MAX) {
  2807. SectionNode->NewSectionIndex = NextSectionIndex;
  2808. NextSectionIndex++;
  2809. }
  2810. //
  2811. // Add fault information to our section record.
  2812. //
  2813. ErrorCode = PfSvAddFaultInfoToSection(ScenarioInfo,
  2814. &LogEntries[EntryIdx],
  2815. SectionNode);
  2816. if (ErrorCode != ERROR_SUCCESS) {
  2817. goto cleanup;
  2818. }
  2819. }
  2820. ErrorCode = ERROR_SUCCESS;
  2821. cleanup:
  2822. if (SectionTable) {
  2823. PFSVC_FREE(SectionTable);
  2824. }
  2825. DBGPR((PFID,PFTRC,"PFSVC: AddTraceInfo()=%x\n", ErrorCode));
  2826. return ErrorCode;
  2827. }
  2828. // FUTURE-2002/03/29-ScottMa -- The function below should not be named
  2829. // PfSvGetSectionRecord, since a PF_SECTION_RECORD is not returned.
  2830. // Consider renaming it to PfSvGetSectionNode.
  2831. PPFSVC_SECTION_NODE
  2832. PfSvGetSectionRecord(
  2833. PPFSVC_SCENARIO_INFO ScenarioInfo,
  2834. WCHAR *FilePath,
  2835. ULONG FilePathLength
  2836. )
  2837. /*++
  2838. Routine Description:
  2839. Find or create a section node in the scenario info for the
  2840. specified file path.
  2841. Arguments:
  2842. ScenarioInfo - Pointer to scenario info structure.
  2843. FilePath - NUL terminated NT path to file.
  2844. FilePathLength - Length of FilePath in chars excluding NUL.
  2845. Return Value:
  2846. Pointer to created or found section node or NULL if there was a
  2847. problem.
  2848. --*/
  2849. {
  2850. PPFSVC_SECTION_NODE SectionNode;
  2851. PLIST_ENTRY HeadEntry;
  2852. PLIST_ENTRY NextEntry;
  2853. LONG ComparisonResult;
  2854. ULONG FilePathSize;
  2855. PPFSVC_SECTION_NODE ReturnNode;
  2856. //
  2857. // Initialize locals.
  2858. //
  2859. ReturnNode = NULL;
  2860. //
  2861. // Walk through the existing sections records looking for a file
  2862. // name match. Section records are on a lexically sorted list.
  2863. //
  2864. HeadEntry = &ScenarioInfo->SectionList;
  2865. NextEntry = HeadEntry->Flink;
  2866. while (HeadEntry != NextEntry) {
  2867. SectionNode = CONTAINING_RECORD(NextEntry,
  2868. PFSVC_SECTION_NODE,
  2869. SectionLink);
  2870. ComparisonResult = wcscmp(SectionNode->FilePath, FilePath);
  2871. if (ComparisonResult == 0) {
  2872. //
  2873. // We found a match. Return this section record.
  2874. //
  2875. ReturnNode = SectionNode;
  2876. goto cleanup;
  2877. } else if (ComparisonResult > 0) {
  2878. //
  2879. // We won't find the name in our list. We have to create a
  2880. // new section record.
  2881. //
  2882. break;
  2883. }
  2884. NextEntry = NextEntry->Flink;
  2885. }
  2886. //
  2887. // We have to create a new section record. NextEntry points to
  2888. // where we have to insert it in the list.
  2889. //
  2890. SectionNode = PfSvChunkAllocatorAllocate(&ScenarioInfo->SectionNodeAllocator);
  2891. if (!SectionNode) {
  2892. ReturnNode = NULL;
  2893. goto cleanup;
  2894. }
  2895. //
  2896. // Initialize the section node.
  2897. //
  2898. SectionNode->FilePath = NULL;
  2899. InitializeListHead(&SectionNode->PageList);
  2900. InitializeListHead(&SectionNode->SectionVolumeLink);
  2901. SectionNode->NewSectionIndex = ULONG_MAX;
  2902. SectionNode->OrgSectionIndex = ULONG_MAX;
  2903. SectionNode->FileIndexNumber.QuadPart = -1i64;
  2904. //
  2905. // Initialize the section record.
  2906. //
  2907. RtlZeroMemory(&SectionNode->SectionRecord, sizeof(PF_SECTION_RECORD));
  2908. //
  2909. // Allocate and copy over the file name.
  2910. //
  2911. FilePathSize = (FilePathLength + 1) * sizeof(WCHAR);
  2912. SectionNode->FilePath = PfSvStringAllocatorAllocate(&ScenarioInfo->PathAllocator,
  2913. FilePathSize);
  2914. if (!SectionNode->FilePath) {
  2915. PfSvCleanupSectionNode(ScenarioInfo, SectionNode);
  2916. PfSvChunkAllocatorFree(&ScenarioInfo->SectionNodeAllocator, SectionNode);
  2917. ReturnNode = NULL;
  2918. goto cleanup;
  2919. }
  2920. RtlCopyMemory(SectionNode->FilePath, FilePath, FilePathSize);
  2921. //
  2922. // Update the file name length on the section record.
  2923. //
  2924. SectionNode->SectionRecord.FileNameLength = FilePathLength;
  2925. //
  2926. // Insert the section into the right spot on the scenario's list.
  2927. //
  2928. InsertTailList(NextEntry, &SectionNode->SectionLink);
  2929. //
  2930. // Return the newly setup section record.
  2931. //
  2932. ReturnNode = SectionNode;
  2933. cleanup:
  2934. return ReturnNode;
  2935. }
  2936. DWORD
  2937. PfSvAddFaultInfoToSection(
  2938. PPFSVC_SCENARIO_INFO ScenarioInfo,
  2939. PPF_LOG_ENTRY LogEntry,
  2940. PPFSVC_SECTION_NODE SectionNode
  2941. )
  2942. /*++
  2943. Routine Description:
  2944. Add fault information from a trace log entry to proper section
  2945. record in the new scenario.
  2946. Arguments:
  2947. ScenarioInfo - Pointer to scenario info structure.
  2948. LogEntry - Pointer to trace log entry.
  2949. SectionNode - Pointer to the section node the log entry belongs to.
  2950. Return Value:
  2951. Win32 error code.
  2952. --*/
  2953. {
  2954. DWORD ErrorCode;
  2955. PPFSVC_PAGE_NODE PageNode;
  2956. PLIST_ENTRY HeadEntry;
  2957. PLIST_ENTRY NextEntry;
  2958. //
  2959. // Walk through the page records for this section.
  2960. //
  2961. HeadEntry = &SectionNode->PageList;
  2962. NextEntry = HeadEntry->Flink;
  2963. while (HeadEntry != NextEntry) {
  2964. PageNode = CONTAINING_RECORD(NextEntry,
  2965. PFSVC_PAGE_NODE,
  2966. PageLink);
  2967. if (PageNode->PageRecord.FileOffset > LogEntry->FileOffset) {
  2968. //
  2969. // We won't find this fault in this sorted list.
  2970. //
  2971. break;
  2972. } else if (PageNode->PageRecord.FileOffset == LogEntry->FileOffset) {
  2973. //
  2974. // We found the page, update the page record and section
  2975. // record with the info in log entry.
  2976. //
  2977. if (LogEntry->IsImage) {
  2978. PageNode->PageRecord.IsImage = 1;
  2979. } else {
  2980. PageNode->PageRecord.IsData = 1;
  2981. }
  2982. //
  2983. // Note the this page was used in this launch.
  2984. //
  2985. PageNode->PageRecord.UsageHistory |= 0x1;
  2986. //
  2987. // See if this page was prefetched for this launch and
  2988. // update appropriate stats.
  2989. //
  2990. if(PageNode->PageRecord.IsIgnore) {
  2991. ScenarioInfo->MissedOpportunityPages++;
  2992. } else {
  2993. ScenarioInfo->HitPages++;
  2994. }
  2995. ErrorCode = ERROR_SUCCESS;
  2996. goto cleanup;
  2997. }
  2998. NextEntry = NextEntry->Flink;
  2999. }
  3000. //
  3001. // We have to add a new page record before NextEntry in the list.
  3002. //
  3003. PageNode = PfSvChunkAllocatorAllocate(&ScenarioInfo->PageNodeAllocator);
  3004. if (!PageNode) {
  3005. ErrorCode = ERROR_NOT_ENOUGH_MEMORY;
  3006. goto cleanup;
  3007. }
  3008. //
  3009. // Set up new page record. First initialize fields.
  3010. //
  3011. PageNode->PageRecord.IsImage = 0;
  3012. PageNode->PageRecord.IsData = 0;
  3013. PageNode->PageRecord.IsIgnore = 0;
  3014. PageNode->PageRecord.FileOffset = LogEntry->FileOffset;
  3015. if (LogEntry->IsImage) {
  3016. PageNode->PageRecord.IsImage = 1;
  3017. } else {
  3018. PageNode->PageRecord.IsData = 1;
  3019. }
  3020. //
  3021. // Initialize usage history for this new page record noting that
  3022. // it was used in this launch.
  3023. //
  3024. PageNode->PageRecord.UsageHistory = 0x1;
  3025. //
  3026. // Initialize prefetch history for this new page record.
  3027. //
  3028. PageNode->PageRecord.PrefetchHistory = 0;
  3029. //
  3030. // Insert it into the sections pages list.
  3031. //
  3032. InsertTailList(NextEntry, &PageNode->PageLink);
  3033. //
  3034. // Update stats on the new scenario.
  3035. //
  3036. ScenarioInfo->NewPages++;
  3037. ErrorCode = ERROR_SUCCESS;
  3038. cleanup:
  3039. return ErrorCode;
  3040. }
  3041. DWORD
  3042. PfSvApplyPrefetchPolicy(
  3043. PPFSVC_SCENARIO_INFO ScenarioInfo
  3044. )
  3045. /*++
  3046. Routine Description:
  3047. Go through all the information in ScenarioInfo and determine which
  3048. pages/sections to prefetch for the next launch of the scenario.
  3049. Arguments:
  3050. ScenarioInfo - Pointer to scenario info structure.
  3051. Return Value:
  3052. Win32 error code.
  3053. --*/
  3054. {
  3055. ULONG Sensitivity;
  3056. PPFSVC_SECTION_NODE SectionNode;
  3057. PPFSVC_PAGE_NODE PageNode;
  3058. ULONG SectNumPagesToPrefetch;
  3059. ULONG HitPages;
  3060. ULONG MissedOpportunityPages;
  3061. ULONG PrefetchedPages;
  3062. ULONG IgnoredPages;
  3063. PLIST_ENTRY SectHead;
  3064. PLIST_ENTRY SectNext;
  3065. PLIST_ENTRY PageHead;
  3066. PLIST_ENTRY PageNext;
  3067. ULONG NumUsed;
  3068. PPF_SCENARIO_HEADER Scenario;
  3069. ULONG FileNameSize;
  3070. ULONG IgnoredFileIdx;
  3071. BOOLEAN bSkipSection;
  3072. PFSV_SUFFIX_COMPARISON_RESULT ComparisonResult;
  3073. DWORD ErrorCode;
  3074. PPFSVC_VOLUME_NODE VolumeNode;
  3075. PWCHAR MFTSuffix;
  3076. PWCHAR PathSuffix;
  3077. FILE_BASIC_INFORMATION FileInformation;
  3078. ULONG MFTSuffixLength;
  3079. //
  3080. // Initialize locals.
  3081. //
  3082. Scenario = &ScenarioInfo->ScenHeader;
  3083. MFTSuffix = L"\\$MFT";
  3084. MFTSuffixLength = wcslen(MFTSuffix);
  3085. DBGPR((PFID,PFTRC,"PFSVC: ApplyPrefetchPolicy()\n"));
  3086. //
  3087. // Initialize fields of the scenario header we will set up.
  3088. //
  3089. Scenario->NumSections = 0;
  3090. Scenario->NumPages = 0;
  3091. Scenario->FileNameInfoSize = 0;
  3092. //
  3093. // Determine sensitivity based on usage of the pages we prefetched
  3094. // and we ignored.
  3095. //
  3096. HitPages = ScenarioInfo->HitPages;
  3097. MissedOpportunityPages = ScenarioInfo->MissedOpportunityPages;
  3098. PrefetchedPages = ScenarioInfo->PrefetchedPages;
  3099. IgnoredPages = ScenarioInfo->IgnoredPages;
  3100. //
  3101. // Check what percent of the pages we brought were used.
  3102. //
  3103. if (PrefetchedPages &&
  3104. (((HitPages * 100) / PrefetchedPages) < PFSVC_MIN_HIT_PERCENTAGE)) {
  3105. //
  3106. // Our hit rate is low. Increase sensitivity of the
  3107. // scenario, so for us to prefetch a page, it has to be
  3108. // used in more of the last launches.
  3109. //
  3110. if (ScenarioInfo->ScenHeader.Sensitivity < PF_MAX_SENSITIVITY) {
  3111. ScenarioInfo->ScenHeader.Sensitivity ++;
  3112. }
  3113. } else if (IgnoredPages &&
  3114. (((MissedOpportunityPages * 100) / IgnoredPages) > PFSVC_MAX_IGNORED_PERCENTAGE)) {
  3115. //
  3116. // If we are using most of what we prefetched (or we are not
  3117. // prefetching anything!), but we ignored some pages we could
  3118. // have prefetched, and they were used too, time to decrease
  3119. // sensitivity so we ignore less pages.
  3120. //
  3121. if (ScenarioInfo->ScenHeader.Sensitivity > PF_MIN_SENSITIVITY) {
  3122. ScenarioInfo->ScenHeader.Sensitivity --;
  3123. }
  3124. }
  3125. //
  3126. // Don't let the boot scenario's sensitivity to fall below 2.
  3127. // This makes sure we don't pick up all the application setup &
  3128. // configuration updates that happen during boot once.
  3129. //
  3130. if (ScenarioInfo->ScenHeader.ScenarioType == PfSystemBootScenarioType) {
  3131. PFSVC_ASSERT(PF_MIN_SENSITIVITY <= 2);
  3132. if (ScenarioInfo->ScenHeader.Sensitivity < 2) {
  3133. ScenarioInfo->ScenHeader.Sensitivity = 2;
  3134. }
  3135. }
  3136. Sensitivity = ScenarioInfo->ScenHeader.Sensitivity;
  3137. //
  3138. // If number of times this scenario was launched is less
  3139. // than sensitivity, adjust sensitivity. Otherwise we
  3140. // won't end up prefetching anything.
  3141. //
  3142. if (Sensitivity > ScenarioInfo->ScenHeader.NumLaunches) {
  3143. Sensitivity = ScenarioInfo->ScenHeader.NumLaunches;
  3144. }
  3145. //
  3146. // Walk through pages for every section and determine if they
  3147. // should be prefetched or not based on scenario sensitivity and
  3148. // their usage history in the last launches.
  3149. //
  3150. SectHead = &ScenarioInfo->SectionList;
  3151. SectNext = SectHead->Flink;
  3152. while (SectHead != SectNext) {
  3153. SectionNode = CONTAINING_RECORD(SectNext,
  3154. PFSVC_SECTION_NODE,
  3155. SectionLink);
  3156. SectNext = SectNext->Flink;
  3157. //
  3158. // Initialize section records fields.
  3159. //
  3160. SectionNode->SectionRecord.IsImage = 0;
  3161. SectionNode->SectionRecord.IsData = 0;
  3162. SectionNode->SectionRecord.NumPages = 0;
  3163. //
  3164. // If we are nearing the limits for number of sections and
  3165. // pages, ignore the rest of the sections.
  3166. //
  3167. if (Scenario->NumSections >= PF_MAXIMUM_SECTIONS ||
  3168. Scenario->NumPages + PF_MAXIMUM_SECTION_PAGES >= PF_MAXIMUM_PAGES) {
  3169. //
  3170. // Remove this section node from our list.
  3171. //
  3172. PfSvCleanupSectionNode(ScenarioInfo, SectionNode);
  3173. RemoveEntryList(&SectionNode->SectionLink);
  3174. PfSvChunkAllocatorFree(&ScenarioInfo->SectionNodeAllocator, SectionNode);
  3175. continue;
  3176. }
  3177. //
  3178. // If this is the boot scenario, check to see if this is one
  3179. // of the sections we ignore.
  3180. //
  3181. if (Scenario->ScenarioType == PfSystemBootScenarioType) {
  3182. bSkipSection = FALSE;
  3183. for (IgnoredFileIdx = 0;
  3184. IgnoredFileIdx < PfSvcGlobals.NumFilesToIgnoreForBoot;
  3185. IgnoredFileIdx++) {
  3186. ComparisonResult = PfSvCompareSuffix(SectionNode->FilePath,
  3187. SectionNode->SectionRecord.FileNameLength,
  3188. PfSvcGlobals.FilesToIgnoreForBoot[IgnoredFileIdx],
  3189. PfSvcGlobals.FileSuffixLengths[IgnoredFileIdx],
  3190. TRUE);
  3191. if (ComparisonResult == PfSvSuffixIdentical) {
  3192. //
  3193. // The suffix matched.
  3194. //
  3195. bSkipSection = TRUE;
  3196. break;
  3197. } else if (ComparisonResult == PfSvSuffixGreaterThan) {
  3198. //
  3199. // Since the ignore-suffices are lexically sorted,
  3200. // this file name's suffix won't match others
  3201. // either.
  3202. //
  3203. bSkipSection = FALSE;
  3204. break;
  3205. }
  3206. }
  3207. if (bSkipSection) {
  3208. //
  3209. // Remove this section node from our list.
  3210. //
  3211. PfSvCleanupSectionNode(ScenarioInfo, SectionNode);
  3212. RemoveEntryList(&SectionNode->SectionLink);
  3213. PfSvChunkAllocatorFree(&ScenarioInfo->SectionNodeAllocator, SectionNode);
  3214. continue;
  3215. }
  3216. }
  3217. //
  3218. // Keep track of num pages to prefetch for this section.
  3219. //
  3220. SectNumPagesToPrefetch = 0;
  3221. PageHead = &SectionNode->PageList;
  3222. PageNext = PageHead->Flink;
  3223. while (PageHead != PageNext) {
  3224. PageNode = CONTAINING_RECORD(PageNext,
  3225. PFSVC_PAGE_NODE,
  3226. PageLink);
  3227. PageNext = PageNext->Flink;
  3228. //
  3229. // Get number of times this page was used in the launches
  3230. // in usage history.
  3231. //
  3232. NumUsed = PfSvGetNumTimesUsed(PageNode->PageRecord.UsageHistory,
  3233. PF_PAGE_HISTORY_SIZE);
  3234. //
  3235. // If it was not used at all in the history we've kept
  3236. // track of, remove it.
  3237. //
  3238. if (NumUsed == 0) {
  3239. RemoveEntryList(&PageNode->PageLink);
  3240. PfSvChunkAllocatorFree(&ScenarioInfo->PageNodeAllocator, PageNode);
  3241. continue;
  3242. }
  3243. //
  3244. // Update the number of pages for this section.
  3245. //
  3246. SectionNode->SectionRecord.NumPages++;
  3247. //
  3248. // Check if this page qualifies to be prefetched next time.
  3249. //
  3250. if (NumUsed >= Sensitivity) {
  3251. PageNode->PageRecord.IsIgnore = 0;
  3252. //
  3253. // Update the number of pages we are prefetching for
  3254. // this section.
  3255. //
  3256. SectNumPagesToPrefetch++;
  3257. //
  3258. // Update whether we are going to prefetch this
  3259. // section as image, data [or both].
  3260. //
  3261. SectionNode->SectionRecord.IsImage |= PageNode->PageRecord.IsImage;
  3262. SectionNode->SectionRecord.IsData |= PageNode->PageRecord.IsData;
  3263. } else {
  3264. PageNode->PageRecord.IsIgnore = 1;
  3265. }
  3266. }
  3267. //
  3268. // Check if we want to keep this section in the scenario:
  3269. //
  3270. bSkipSection = FALSE;
  3271. if (SectionNode->SectionRecord.NumPages == 0) {
  3272. //
  3273. // If we don't have any pages left for this section, remove
  3274. // it.
  3275. //
  3276. bSkipSection = TRUE;
  3277. } else if (SectionNode->SectionRecord.NumPages >= PF_MAXIMUM_SECTION_PAGES) {
  3278. //
  3279. // If we ended up with too many pages for this section, remove
  3280. // it.
  3281. //
  3282. bSkipSection = TRUE;
  3283. } else if (PfSvcGlobals.CSCRootPath &&
  3284. wcsstr(SectionNode->FilePath, PfSvcGlobals.CSCRootPath)) {
  3285. //
  3286. // Skip client side cache (CSC) files. These files may get encrypted as
  3287. // LocalSystem, and when the AppData folder is redirected, we may take
  3288. // minutes trying to open them when prefetching for shell launch.
  3289. //
  3290. bSkipSection = TRUE;
  3291. } else {
  3292. //
  3293. // Encrypted files may result in several network accesses during open,
  3294. // even if they are local. This is especially so if the AppData folder is
  3295. // redirected to a server. We cannot afford these network delays when
  3296. // blocking the scenario for prefetching.
  3297. //
  3298. ErrorCode = PfSvGetFileBasicInformation(SectionNode->FilePath,
  3299. &FileInformation);
  3300. if ((ErrorCode == ERROR_SUCCESS) &&
  3301. (FileInformation.FileAttributes & FILE_ATTRIBUTE_ENCRYPTED)) {
  3302. bSkipSection = TRUE;
  3303. }
  3304. }
  3305. if (bSkipSection) {
  3306. PfSvCleanupSectionNode(ScenarioInfo, SectionNode);
  3307. RemoveEntryList(&SectionNode->SectionLink);
  3308. PfSvChunkAllocatorFree(&ScenarioInfo->SectionNodeAllocator, SectionNode);
  3309. continue;
  3310. }
  3311. //
  3312. // Get the volume node for the volume this section is
  3313. // on. The volume node should have been added when the
  3314. // existing scenario information or the new trace
  3315. // information was added to the scenario info.
  3316. //
  3317. VolumeNode = PfSvGetVolumeNode(ScenarioInfo,
  3318. SectionNode->FilePath,
  3319. SectionNode->SectionRecord.FileNameLength);
  3320. PFSVC_ASSERT(VolumeNode);
  3321. if (VolumeNode) {
  3322. VolumeNode->NumAllSections++;
  3323. }
  3324. //
  3325. // If we are not prefetching any pages from this section for
  3326. // the next launch, mark it ignore.
  3327. //
  3328. if (SectNumPagesToPrefetch == 0) {
  3329. SectionNode->SectionRecord.IsIgnore = 1;
  3330. } else {
  3331. SectionNode->SectionRecord.IsIgnore = 0;
  3332. //
  3333. // If this is MFT section for this volume, save it on the volume
  3334. // node. We will add the pages referenced from MFT to the list of
  3335. // files to prefetch metadata for.
  3336. //
  3337. if ((VolumeNode && VolumeNode->MFTSectionNode == NULL) &&
  3338. (VolumeNode->VolumePathLength == (SectionNode->SectionRecord.FileNameLength - MFTSuffixLength))) {
  3339. PathSuffix = SectionNode->FilePath + SectionNode->SectionRecord.FileNameLength;
  3340. PathSuffix -= MFTSuffixLength;
  3341. if (wcscmp(PathSuffix, MFTSuffix) == 0) {
  3342. //
  3343. // This is the MFT section node for this volume.
  3344. //
  3345. VolumeNode->MFTSectionNode = SectionNode;
  3346. //
  3347. // Mark the MFT section node as "ignore" so kernel does
  3348. // not attempt to prefetch it directly.
  3349. //
  3350. VolumeNode->MFTSectionNode->SectionRecord.IsIgnore = 1;
  3351. //
  3352. // Save how many pages we'll prefetch from MFT on the section
  3353. // node. We save this instead of FileIndexNumber field, since
  3354. // there won't be one for MFT. We won't try to get one either
  3355. // since we are marking this section node ignore.
  3356. //
  3357. VolumeNode->MFTSectionNode->MFTNumPagesToPrefetch = SectNumPagesToPrefetch;
  3358. }
  3359. }
  3360. }
  3361. //
  3362. // If we are not ignoring this section, update its file system
  3363. // index number so its metadata can be prefetched.
  3364. //
  3365. if (SectionNode->SectionRecord.IsIgnore == 0) {
  3366. ErrorCode = PfSvGetFileIndexNumber(SectionNode->FilePath,
  3367. &SectionNode->FileIndexNumber);
  3368. if (ErrorCode == ERROR_SUCCESS) {
  3369. if (VolumeNode) {
  3370. //
  3371. // Insert this section node into the section list of
  3372. // the volume it is on.
  3373. //
  3374. InsertTailList(&VolumeNode->SectionList,
  3375. &SectionNode->SectionVolumeLink);
  3376. VolumeNode->NumSections++;
  3377. //
  3378. // Update volume node's directory list with parent
  3379. // directories of this file.
  3380. //
  3381. PfSvAddParentDirectoriesToList(&VolumeNode->DirectoryList,
  3382. VolumeNode->VolumePathLength,
  3383. SectionNode->FilePath,
  3384. SectionNode->SectionRecord.FileNameLength);
  3385. }
  3386. }
  3387. }
  3388. //
  3389. // Update number of sections, number of pages and file name
  3390. // info length on the scenario.
  3391. //
  3392. Scenario->NumSections++;
  3393. Scenario->NumPages += SectionNode->SectionRecord.NumPages;
  3394. FileNameSize = sizeof(WCHAR) *
  3395. (SectionNode->SectionRecord.FileNameLength + 1);
  3396. Scenario->FileNameInfoSize += FileNameSize;
  3397. }
  3398. //
  3399. // We are done.
  3400. //
  3401. ErrorCode = ERROR_SUCCESS;
  3402. DBGPR((PFID,PFTRC,"PFSVC: ApplyPrefetchPolicy()=%x\n", ErrorCode));
  3403. return ErrorCode;
  3404. }
  3405. ULONG
  3406. PfSvGetNumTimesUsed(
  3407. ULONG UsageHistory,
  3408. ULONG UsageHistorySize
  3409. )
  3410. /*++
  3411. Routine Description:
  3412. Calculate how many times a page seems to be used according to
  3413. UsageHistory.
  3414. Arguments:
  3415. UsageHistory - Bitmap. 1's correspond to "was used", 0 = "not used".
  3416. UsageHistorySize - Size of UsageHistory in bits from the least
  3417. significant bit.
  3418. Return Value:
  3419. How many times the page seems to be used.
  3420. --*/
  3421. {
  3422. ULONG NumUsed;
  3423. ULONG BitIdx;
  3424. //
  3425. // Initialize locals.
  3426. //
  3427. NumUsed = 0;
  3428. //
  3429. // Walk through the bits in usage history starting from the least
  3430. // significant and count how many bits are on. We can probably do
  3431. // this more efficiently.
  3432. //
  3433. // FUTURE-2002/03/29-ScottMa -- You're right... We CAN do this more
  3434. // efficiently!
  3435. for (BitIdx = 0; BitIdx < UsageHistorySize; BitIdx++) {
  3436. if (UsageHistory & (1 << BitIdx)) {
  3437. NumUsed++;
  3438. }
  3439. }
  3440. return NumUsed;
  3441. }
  3442. ULONG
  3443. PfSvGetTraceEndIdx(
  3444. PPF_TRACE_HEADER Trace
  3445. )
  3446. /*++
  3447. Routine Description:
  3448. Determines the index of the last page logged in the trace.
  3449. Arguments:
  3450. Trace - Pointer to trace.
  3451. Return Value:
  3452. Index of the last page logged.
  3453. --*/
  3454. {
  3455. ULONG TotalFaults;
  3456. ULONG PeriodIdx;
  3457. ULONG *Id;
  3458. DBGPR((PFID,PFSTRC,"PFSVC: GetTraceEndIdx(%p)\n", Trace));
  3459. TotalFaults = Trace->FaultsPerPeriod[0];
  3460. for (PeriodIdx = 1; PeriodIdx < PF_MAX_NUM_TRACE_PERIODS; PeriodIdx++) {
  3461. if(Trace->FaultsPerPeriod[PeriodIdx] < PFSVC_MIN_FAULT_THRESHOLD) {
  3462. //
  3463. // If this is not the boot scenario, determine that
  3464. // scenario has ended when logged pagefaults for a time
  3465. // slice falls below minimum.
  3466. //
  3467. if (Trace->ScenarioType != PfSystemBootScenarioType) {
  3468. break;
  3469. }
  3470. }
  3471. TotalFaults += Trace->FaultsPerPeriod[PeriodIdx];
  3472. }
  3473. //
  3474. // Sum of entries per period should not be greater than all
  3475. // entries logged.
  3476. //
  3477. PFSVC_ASSERT(TotalFaults <= Trace->NumEntries);
  3478. DBGPR((PFID,PFSTRC,"PFSVC: GetTraceEndIdx(%p)=%d\n", Trace, TotalFaults));
  3479. return TotalFaults;
  3480. }
  3481. //
  3482. // Routines to write updated scenario instructions to the scenario
  3483. // file.
  3484. //
  3485. DWORD
  3486. PfSvWriteScenario(
  3487. PPFSVC_SCENARIO_INFO ScenarioInfo,
  3488. PWCHAR ScenarioFilePath
  3489. )
  3490. /*++
  3491. Routine Description:
  3492. Prepare scenario instructions structure from the scenarion info
  3493. and write it to the specified file.
  3494. Arguments:
  3495. ScenarioInfo - Pointer to scenario info structure.
  3496. ScenarioFilePath - Path to scenarion file to update.
  3497. Return Value:
  3498. Win32 error code.
  3499. --*/
  3500. {
  3501. DWORD ErrorCode;
  3502. PPF_SCENARIO_HEADER Scenario;
  3503. ULONG FailedCheck;
  3504. //
  3505. // Initialize locals.
  3506. //
  3507. Scenario = NULL;
  3508. DBGPR((PFID,PFTRC,"PFSVC: WriteScenario(%ws)\n", ScenarioFilePath));
  3509. //
  3510. // Build scenario dump from information we gathered.
  3511. //
  3512. ErrorCode = PfSvPrepareScenarioDump(ScenarioInfo, &Scenario);
  3513. if (ErrorCode != ERROR_SUCCESS) {
  3514. goto cleanup;
  3515. }
  3516. //
  3517. // Make sure the scenario we built passes the checks.
  3518. //
  3519. if (!PfSvVerifyScenarioBuffer(Scenario, Scenario->Size, &FailedCheck) ||
  3520. Scenario->ServiceVersion != PFSVC_SERVICE_VERSION) {
  3521. PFSVC_ASSERT(FALSE);
  3522. ErrorCode = ERROR_BAD_FORMAT;
  3523. goto cleanup;
  3524. }
  3525. //
  3526. // Write out the buffer.
  3527. //
  3528. ErrorCode = PfSvWriteBuffer(ScenarioFilePath, Scenario, Scenario->Size);
  3529. //
  3530. // Fall through with ErrorCode.
  3531. //
  3532. cleanup:
  3533. if (Scenario) {
  3534. PFSVC_FREE(Scenario);
  3535. }
  3536. DBGPR((PFID,PFTRC,"PFSVC: WriteScenario(%ws)=%x\n", ScenarioFilePath, ErrorCode));
  3537. return ErrorCode;
  3538. }
  3539. DWORD
  3540. PfSvPrepareScenarioDump(
  3541. IN PPFSVC_SCENARIO_INFO ScenarioInfo,
  3542. OUT PPF_SCENARIO_HEADER *ScenarioPtr
  3543. )
  3544. /*++
  3545. Routine Description:
  3546. Allocate a contiguous scenario buffer and fill it in with
  3547. information in ScenarioInfo. ScenarioInfo is not modified.
  3548. Arguments:
  3549. ScenarioInfo - Pointer to scenario information built from an
  3550. existing scenario file and a scenario trace.
  3551. ScenarioPtr - If successful, pointer to allocated and built
  3552. scenario is put here. The caller should free this buffer when
  3553. done with it.
  3554. Return Value:
  3555. Win32 error code.
  3556. --*/
  3557. {
  3558. PPF_SCENARIO_HEADER Scenario;
  3559. ULONG Size;
  3560. DWORD ErrorCode;
  3561. PPF_SECTION_RECORD Sections;
  3562. PPF_SECTION_RECORD Section;
  3563. PPFSVC_SECTION_NODE SectionNode;
  3564. ULONG CurSectionIdx;
  3565. PPF_PAGE_RECORD Pages;
  3566. PPF_PAGE_RECORD Page;
  3567. PPF_PAGE_RECORD PreviousPage;
  3568. PPFSVC_PAGE_NODE PageNode;
  3569. ULONG CurPageIdx;
  3570. PCHAR FileNames;
  3571. ULONG CurFileInfoOffset;
  3572. PCHAR DestPtr;
  3573. PLIST_ENTRY SectHead;
  3574. PLIST_ENTRY SectNext;
  3575. PLIST_ENTRY PageHead;
  3576. PLIST_ENTRY PageNext;
  3577. ULONG FileNameSize;
  3578. PPFSVC_VOLUME_NODE VolumeNode;
  3579. PLIST_ENTRY HeadVolume;
  3580. PLIST_ENTRY NextVolume;
  3581. PCHAR MetadataInfoBase;
  3582. PPF_METADATA_RECORD MetadataRecordTable;
  3583. PPF_METADATA_RECORD MetadataRecord;
  3584. ULONG MetadataInfoSize;
  3585. ULONG NumMetadataRecords;
  3586. ULONG CurMetadataRecordIdx;
  3587. ULONG CopySize;
  3588. ULONG CurFilePrefetchIdx;
  3589. ULONG FilePrefetchInfoSize;
  3590. ULONG FilePrefetchCount;
  3591. PFILE_PREFETCH FilePrefetchInfo;
  3592. WCHAR *DirectoryPath;
  3593. ULONG DirectoryPathLength;
  3594. PPFSVC_PATH PathEntry;
  3595. LARGE_INTEGER IndexNumber;
  3596. ULONG DirectoryPathInfoSize;
  3597. ULONG DirectoryPathSize;
  3598. PPF_COUNTED_STRING DirectoryPathCS;
  3599. //
  3600. // Initialize locals.
  3601. //
  3602. Scenario = NULL;
  3603. DBGPR((PFID,PFTRC,"PFSVC: PrepareScenarioDump()\n"));
  3604. //
  3605. // Calculate how big the scenario is going to be.
  3606. //
  3607. Size = sizeof(PF_SCENARIO_HEADER);
  3608. Size += ScenarioInfo->ScenHeader.NumSections * sizeof(PF_SECTION_RECORD);
  3609. Size += ScenarioInfo->ScenHeader.NumPages * sizeof(PF_PAGE_RECORD);
  3610. Size += ScenarioInfo->ScenHeader.FileNameInfoSize;
  3611. //
  3612. // Add space for the metadata prefetch information.
  3613. //
  3614. //
  3615. // Make some space for aligning the metadata records table.
  3616. //
  3617. MetadataInfoSize = _alignof(PF_METADATA_RECORD);
  3618. HeadVolume = &ScenarioInfo->VolumeList;
  3619. NextVolume = HeadVolume->Flink;
  3620. NumMetadataRecords = 0;
  3621. while (NextVolume != HeadVolume) {
  3622. VolumeNode = CONTAINING_RECORD(NextVolume,
  3623. PFSVC_VOLUME_NODE,
  3624. VolumeLink);
  3625. NextVolume = NextVolume->Flink;
  3626. //
  3627. // If there are no sections at all on this volume, skip it.
  3628. //
  3629. if (VolumeNode->NumAllSections == 0) {
  3630. continue;
  3631. }
  3632. NumMetadataRecords++;
  3633. //
  3634. // Metadata record:
  3635. //
  3636. MetadataInfoSize += sizeof(PF_METADATA_RECORD);
  3637. //
  3638. // Volume Path:
  3639. //
  3640. MetadataInfoSize += (VolumeNode->VolumePathLength + 1) * sizeof(WCHAR);
  3641. //
  3642. // FilePrefetchInfo buffer: This has to be ULONGLONG
  3643. // aligned. Add extra space for that in case.
  3644. //
  3645. MetadataInfoSize += _alignof(FILE_PREFETCH);
  3646. MetadataInfoSize += sizeof(FILE_PREFETCH);
  3647. if (VolumeNode->NumSections) {
  3648. MetadataInfoSize += (VolumeNode->NumSections - 1) * sizeof(ULONGLONG);
  3649. }
  3650. MetadataInfoSize += VolumeNode->DirectoryList.NumPaths * sizeof(ULONGLONG);
  3651. if (VolumeNode->MFTSectionNode) {
  3652. MetadataInfoSize += VolumeNode->MFTSectionNode->MFTNumPagesToPrefetch * sizeof(ULONGLONG);
  3653. }
  3654. //
  3655. // Add space for the directory paths on this volume.
  3656. //
  3657. MetadataInfoSize += VolumeNode->DirectoryList.NumPaths * sizeof(PF_COUNTED_STRING);
  3658. MetadataInfoSize += VolumeNode->DirectoryList.TotalLength * sizeof(WCHAR);
  3659. //
  3660. // Note that PF_COUNTED_STRING contains space for one
  3661. // character. DirectoryList's total length excludes NUL's at
  3662. // the end of each path.
  3663. //
  3664. }
  3665. Size += MetadataInfoSize;
  3666. //
  3667. // Allocate scenario buffer.
  3668. //
  3669. Scenario = PFSVC_ALLOC(Size);
  3670. if (!Scenario) {
  3671. ErrorCode = ERROR_NOT_ENOUGH_MEMORY;
  3672. goto cleanup;
  3673. }
  3674. //
  3675. // Copy the header and set the size.
  3676. //
  3677. *Scenario = ScenarioInfo->ScenHeader;
  3678. Scenario->Size = Size;
  3679. DestPtr = (PCHAR) Scenario + sizeof(*Scenario);
  3680. //
  3681. // Initialize where our data is going.
  3682. //
  3683. Sections = (PPF_SECTION_RECORD) DestPtr;
  3684. Scenario->SectionInfoOffset = (ULONG) (DestPtr - (PCHAR) Scenario);
  3685. CurSectionIdx = 0;
  3686. DestPtr += Scenario->NumSections * sizeof(PF_SECTION_RECORD);
  3687. Pages = (PPF_PAGE_RECORD) DestPtr;
  3688. Scenario->PageInfoOffset = (ULONG) (DestPtr - (PCHAR) Scenario);
  3689. CurPageIdx = 0;
  3690. DestPtr += Scenario->NumPages * sizeof(PF_PAGE_RECORD);
  3691. FileNames = DestPtr;
  3692. Scenario->FileNameInfoOffset = (ULONG) (DestPtr - (PCHAR) Scenario);
  3693. CurFileInfoOffset = 0;
  3694. DestPtr += Scenario->FileNameInfoSize;
  3695. //
  3696. // Extra space for this alignment was allocated upfront.
  3697. //
  3698. PFSVC_ASSERT(PF_IS_POWER_OF_TWO(_alignof(PF_METADATA_RECORD)));
  3699. MetadataInfoBase = PF_ALIGN_UP(DestPtr, _alignof(PF_METADATA_RECORD));
  3700. DestPtr += MetadataInfoSize;
  3701. MetadataRecordTable = (PPF_METADATA_RECORD) MetadataInfoBase;
  3702. Scenario->MetadataInfoOffset = (ULONG) (MetadataInfoBase - (PCHAR) Scenario);
  3703. Scenario->MetadataInfoSize = (ULONG) (DestPtr - MetadataInfoBase);
  3704. Scenario->NumMetadataRecords = NumMetadataRecords;
  3705. //
  3706. // Destination pointer should be at the end of the allocated
  3707. // buffer now.
  3708. //
  3709. if (DestPtr != (PCHAR) Scenario + Scenario->Size) {
  3710. PFSVC_ASSERT(FALSE);
  3711. ErrorCode = ERROR_BAD_FORMAT;
  3712. goto cleanup;
  3713. }
  3714. //
  3715. // Walk through the sections on the new scenario info and copy
  3716. // them.
  3717. //
  3718. SectHead = &ScenarioInfo->SectionList;
  3719. SectNext = SectHead->Flink;
  3720. while (SectHead != SectNext) {
  3721. SectionNode = CONTAINING_RECORD(SectNext,
  3722. PFSVC_SECTION_NODE,
  3723. SectionLink);
  3724. //
  3725. // The target section record.
  3726. //
  3727. Section = &Sections[CurSectionIdx];
  3728. //
  3729. // Copy section record info.
  3730. //
  3731. *Section = SectionNode->SectionRecord;
  3732. //
  3733. // Copy pages for the section.
  3734. //
  3735. Section->FirstPageIdx = PF_INVALID_PAGE_IDX;
  3736. PreviousPage = NULL;
  3737. PageHead = &SectionNode->PageList;
  3738. PageNext = PageHead->Flink;
  3739. while (PageNext != PageHead) {
  3740. PageNode = CONTAINING_RECORD(PageNext,
  3741. PFSVC_PAGE_NODE,
  3742. PageLink);
  3743. Page = &Pages[CurPageIdx];
  3744. //
  3745. // If this is the first page in the section, update first
  3746. // page index on the section record.
  3747. //
  3748. if (Section->FirstPageIdx == PF_INVALID_PAGE_IDX) {
  3749. Section->FirstPageIdx = CurPageIdx;
  3750. }
  3751. //
  3752. // Copy page record.
  3753. //
  3754. *Page = PageNode->PageRecord;
  3755. //
  3756. // Update NextPageIdx on the previous page if there is
  3757. // one.
  3758. //
  3759. if (PreviousPage) {
  3760. PreviousPage->NextPageIdx = CurPageIdx;
  3761. }
  3762. //
  3763. // Update previous page.
  3764. //
  3765. PreviousPage = Page;
  3766. //
  3767. // Set next link to list termination now. If there is a
  3768. // next page it is going to update this.
  3769. //
  3770. Page->NextPageIdx = PF_INVALID_PAGE_IDX;
  3771. //
  3772. // Update position in the page record table.
  3773. //
  3774. CurPageIdx++;
  3775. PFSVC_ASSERT(CurPageIdx <= Scenario->NumPages);
  3776. PageNext = PageNext->Flink;
  3777. }
  3778. //
  3779. // Copy over file name.
  3780. //
  3781. FileNameSize = (Section->FileNameLength + 1) * sizeof(WCHAR);
  3782. RtlCopyMemory(FileNames + CurFileInfoOffset,
  3783. SectionNode->FilePath,
  3784. FileNameSize);
  3785. //
  3786. // Update section record's file name offset.
  3787. //
  3788. Section->FileNameOffset = CurFileInfoOffset;
  3789. //
  3790. // Update current index into file name info.
  3791. //
  3792. CurFileInfoOffset += FileNameSize;
  3793. PFSVC_ASSERT(CurFileInfoOffset <= Scenario->FileNameInfoSize);
  3794. //
  3795. // Update our position in the section table.
  3796. //
  3797. CurSectionIdx++;
  3798. PFSVC_ASSERT(CurSectionIdx <= Scenario->NumSections);
  3799. SectNext = SectNext->Flink;
  3800. }
  3801. //
  3802. // Make sure we filled up the tables.
  3803. //
  3804. if (CurSectionIdx != Scenario->NumSections ||
  3805. CurPageIdx != Scenario->NumPages ||
  3806. CurFileInfoOffset != Scenario->FileNameInfoSize) {
  3807. PFSVC_ASSERT(FALSE);
  3808. ErrorCode = ERROR_BAD_FORMAT;
  3809. goto cleanup;
  3810. }
  3811. //
  3812. // Build and copy the metadata prefetch information.
  3813. //
  3814. //
  3815. // Set our target to right after the metadata records table.
  3816. //
  3817. DestPtr = MetadataInfoBase + sizeof(PF_METADATA_RECORD) * NumMetadataRecords;
  3818. CurMetadataRecordIdx = 0;
  3819. HeadVolume = &ScenarioInfo->VolumeList;
  3820. NextVolume = HeadVolume->Flink;
  3821. while (NextVolume != HeadVolume) {
  3822. VolumeNode = CONTAINING_RECORD(NextVolume,
  3823. PFSVC_VOLUME_NODE,
  3824. VolumeLink);
  3825. NextVolume = NextVolume->Flink;
  3826. //
  3827. // If there are no sections at all on this volume, skip it.
  3828. //
  3829. if (VolumeNode->NumAllSections == 0) {
  3830. continue;
  3831. }
  3832. //
  3833. // Make sure we are within bounds.
  3834. //
  3835. if (CurMetadataRecordIdx >= NumMetadataRecords) {
  3836. PFSVC_ASSERT(CurMetadataRecordIdx < NumMetadataRecords);
  3837. ErrorCode = ERROR_BAD_FORMAT;
  3838. goto cleanup;
  3839. }
  3840. MetadataRecord = &MetadataRecordTable[CurMetadataRecordIdx];
  3841. CurMetadataRecordIdx++;
  3842. //
  3843. // Copy volume identifiers.
  3844. //
  3845. MetadataRecord->SerialNumber = VolumeNode->SerialNumber;
  3846. MetadataRecord->CreationTime = VolumeNode->CreationTime;
  3847. //
  3848. // Copy volume name.
  3849. //
  3850. MetadataRecord->VolumeNameOffset = (ULONG) (DestPtr - MetadataInfoBase);
  3851. MetadataRecord->VolumeNameLength = VolumeNode->VolumePathLength;
  3852. CopySize = (VolumeNode->VolumePathLength + 1) * sizeof(WCHAR);
  3853. if (DestPtr + CopySize > (PCHAR) Scenario + Scenario->Size) {
  3854. PFSVC_ASSERT(FALSE);
  3855. ErrorCode = ERROR_BAD_FORMAT;
  3856. goto cleanup;
  3857. }
  3858. RtlCopyMemory(DestPtr, VolumeNode->VolumePath, CopySize);
  3859. DestPtr += CopySize;
  3860. //
  3861. // Align and update DestPtr for the FILE_PREFETCH structure.
  3862. //
  3863. PFSVC_ASSERT(PF_IS_POWER_OF_TWO(_alignof(FILE_PREFETCH)));
  3864. DestPtr = PF_ALIGN_UP(DestPtr, _alignof(FILE_PREFETCH));
  3865. FilePrefetchInfo = (PFILE_PREFETCH) DestPtr;
  3866. MetadataRecord->FilePrefetchInfoOffset = (ULONG) (DestPtr - MetadataInfoBase);
  3867. //
  3868. // Calculate size of the file prefetch information structure.
  3869. //
  3870. FilePrefetchCount = VolumeNode->NumSections;
  3871. if (VolumeNode->MFTSectionNode) {
  3872. FilePrefetchCount += VolumeNode->MFTSectionNode->MFTNumPagesToPrefetch;
  3873. }
  3874. FilePrefetchCount += VolumeNode->DirectoryList.NumPaths;
  3875. FilePrefetchInfoSize = sizeof(FILE_PREFETCH);
  3876. if (FilePrefetchCount) {
  3877. //
  3878. // Note that there is room for one entry in the FILE_PREFETCH
  3879. // structure.
  3880. //
  3881. FilePrefetchInfoSize += (FilePrefetchCount - 1) * sizeof(ULONGLONG);
  3882. }
  3883. MetadataRecord->FilePrefetchInfoSize = FilePrefetchInfoSize;
  3884. if (DestPtr + FilePrefetchInfoSize > (PCHAR) Scenario + Scenario->Size) {
  3885. PFSVC_ASSERT(FALSE);
  3886. ErrorCode = ERROR_BAD_FORMAT;
  3887. goto cleanup;
  3888. }
  3889. //
  3890. // Update destination pointer.
  3891. //
  3892. DestPtr += FilePrefetchInfoSize;
  3893. //
  3894. // Initialize file prefetch information structure.
  3895. //
  3896. FilePrefetchInfo->Type = FILE_PREFETCH_TYPE_FOR_CREATE;
  3897. FilePrefetchInfo->Count = FilePrefetchCount;
  3898. //
  3899. // Build list of file indexes to prefetch:
  3900. //
  3901. CurFilePrefetchIdx = 0;
  3902. //
  3903. // Add file system index numbers for sections.
  3904. //
  3905. SectHead = &VolumeNode->SectionList;
  3906. SectNext = SectHead->Flink;
  3907. while(SectNext != SectHead) {
  3908. SectionNode = CONTAINING_RECORD(SectNext,
  3909. PFSVC_SECTION_NODE,
  3910. SectionVolumeLink);
  3911. SectNext = SectNext->Flink;
  3912. if (CurFilePrefetchIdx >= VolumeNode->NumSections) {
  3913. PFSVC_ASSERT(FALSE);
  3914. ErrorCode = ERROR_BAD_FORMAT;
  3915. goto cleanup;
  3916. }
  3917. //
  3918. // Add the filesystem index number for this section to the list.
  3919. //
  3920. FilePrefetchInfo->Prefetch[CurFilePrefetchIdx] =
  3921. SectionNode->FileIndexNumber.QuadPart;
  3922. CurFilePrefetchIdx++;
  3923. }
  3924. //
  3925. // Add file system index numbers for directories.
  3926. //
  3927. PathEntry = NULL;
  3928. while (PathEntry = PfSvGetNextPathSorted(&VolumeNode->DirectoryList,
  3929. PathEntry)) {
  3930. DirectoryPath = PathEntry->Path;
  3931. //
  3932. // Get the file index number for this directory and add it
  3933. // to the list we'll ask the filesystem to prefetch.
  3934. //
  3935. ErrorCode = PfSvGetFileIndexNumber(DirectoryPath, &IndexNumber);
  3936. if (ErrorCode == ERROR_SUCCESS) {
  3937. FilePrefetchInfo->Prefetch[CurFilePrefetchIdx] = IndexNumber.QuadPart;
  3938. } else {
  3939. FilePrefetchInfo->Prefetch[CurFilePrefetchIdx] = 0;
  3940. }
  3941. CurFilePrefetchIdx++;
  3942. }
  3943. //
  3944. // Add file system index numbers that we drive from direct MFT access.
  3945. //
  3946. if (VolumeNode->MFTSectionNode) {
  3947. SectionNode = VolumeNode->MFTSectionNode;
  3948. for (PageNext = SectionNode->PageList.Flink;
  3949. PageNext != &SectionNode->PageList;
  3950. PageNext = PageNext->Flink) {
  3951. PageNode = CONTAINING_RECORD(PageNext,
  3952. PFSVC_PAGE_NODE,
  3953. PageLink);
  3954. if (!PageNode->PageRecord.IsIgnore) {
  3955. //
  3956. // We know the file offset in MFT. Every file record is
  3957. // 1KB == 2^10 bytes. To convert fileoffset in MFT to a
  3958. // file record number we just shift it by 10.
  3959. //
  3960. FilePrefetchInfo->Prefetch[CurFilePrefetchIdx] =
  3961. PageNode->PageRecord.FileOffset >> 10;
  3962. CurFilePrefetchIdx++;
  3963. }
  3964. }
  3965. }
  3966. //
  3967. // We should have specified all the file index numbers.
  3968. //
  3969. PFSVC_ASSERT(CurFilePrefetchIdx == FilePrefetchInfo->Count);
  3970. //
  3971. // Add paths for directories accessed on this volume.
  3972. //
  3973. MetadataRecord->NumDirectories = VolumeNode->DirectoryList.NumPaths;
  3974. MetadataRecord->DirectoryPathsOffset = (ULONG)(DestPtr - MetadataInfoBase);
  3975. PathEntry = NULL;
  3976. while (PathEntry = PfSvGetNextPathSorted(&VolumeNode->DirectoryList,
  3977. PathEntry)) {
  3978. DirectoryPath = PathEntry->Path;
  3979. DirectoryPathLength = PathEntry->Length;
  3980. //
  3981. // Calculate how big the entry for this path is going to
  3982. // be and make sure it will be within bounds.
  3983. //
  3984. DirectoryPathSize = sizeof(PF_COUNTED_STRING);
  3985. DirectoryPathSize += DirectoryPathLength * sizeof(WCHAR);
  3986. if (DestPtr + DirectoryPathSize > (PCHAR) Scenario + Scenario->Size) {
  3987. PFSVC_ASSERT(FALSE);
  3988. ErrorCode = ERROR_BAD_FORMAT;
  3989. goto cleanup;
  3990. }
  3991. //
  3992. // Copy over the directory path.
  3993. //
  3994. DirectoryPathCS = (PPF_COUNTED_STRING) DestPtr;
  3995. DirectoryPathCS->Length = (USHORT) DirectoryPathLength;
  3996. RtlCopyMemory(DirectoryPathCS->String,
  3997. DirectoryPath,
  3998. (DirectoryPathLength + 1) * sizeof(WCHAR));
  3999. DestPtr += DirectoryPathSize;
  4000. }
  4001. }
  4002. //
  4003. // Make sure we are not past the end of the buffer.
  4004. //
  4005. if (DestPtr > (PCHAR) Scenario + Scenario->Size) {
  4006. PFSVC_ASSERT(FALSE);
  4007. ErrorCode = ERROR_BAD_FORMAT;
  4008. goto cleanup;
  4009. }
  4010. //
  4011. // Set up return pointer.
  4012. //
  4013. *ScenarioPtr = Scenario;
  4014. ErrorCode = ERROR_SUCCESS;
  4015. cleanup:
  4016. if (ErrorCode != ERROR_SUCCESS) {
  4017. if (Scenario != NULL) {
  4018. PFSVC_FREE(Scenario);
  4019. }
  4020. }
  4021. DBGPR((PFID,PFTRC,"PFSVC: PrepareScenarioDump()=%x\n", ErrorCode));
  4022. return ErrorCode;
  4023. }
  4024. //
  4025. // Routines to maintain the optimal disk layout file and update disk
  4026. // layout.
  4027. //
  4028. DWORD
  4029. PfSvUpdateOptimalLayout(
  4030. PPFSVC_IDLE_TASK Task
  4031. )
  4032. /*++
  4033. Routine Description:
  4034. This routine will determine if the optimal disk layout has to be
  4035. updated and if so it will write out a new layout file and launch
  4036. the defragger.
  4037. Arguments:
  4038. Task - If specified the function will check Task every once in a
  4039. while to see if it should exit with ERROR_RETRY.
  4040. Return Value:
  4041. Win32 error code.
  4042. --*/
  4043. {
  4044. ULARGE_INTEGER CurrentTimeLI;
  4045. ULARGE_INTEGER LastDiskLayoutTimeLI;
  4046. ULARGE_INTEGER MinTimeBeforeRelayoutLI;
  4047. PFSVC_PATH_LIST OptimalLayout;
  4048. PFSVC_PATH_LIST CurrentLayout;
  4049. FILETIME LastDiskLayoutTime;
  4050. FILETIME LayoutFileTime;
  4051. FILETIME CurrentTime;
  4052. PPFSVC_PATH_LIST NewLayout;
  4053. PWCHAR LayoutFilePath;
  4054. ULONG LayoutFilePathBufferSize;
  4055. DWORD ErrorCode;
  4056. DWORD BootScenarioProcessed;
  4057. DWORD BootFilesWereOptimized;
  4058. DWORD MinHoursBeforeRelayout;
  4059. DWORD Size;
  4060. DWORD RegValueType;
  4061. BOOLEAN LayoutChanged;
  4062. BOOLEAN MissingOriginalLayoutFile;
  4063. BOOLEAN CheckForLayoutFrequencyLimit;
  4064. //
  4065. // Initialize locals.
  4066. //
  4067. LayoutFilePath = NULL;
  4068. LayoutFilePathBufferSize = 0;
  4069. PfSvInitializePathList(&OptimalLayout, NULL, FALSE);
  4070. PfSvInitializePathList(&CurrentLayout, NULL, FALSE);
  4071. DBGPR((PFID,PFTRC,"PFSVC: UpdateOptimalLayout(%p)\n", Task));
  4072. //
  4073. // Determine when we updated the disk layout from the layout file.
  4074. //
  4075. ErrorCode = PfSvGetLastDiskLayoutTime(&LastDiskLayoutTime);
  4076. if (ErrorCode != ERROR_SUCCESS) {
  4077. goto cleanup;
  4078. }
  4079. //
  4080. // Query whether boot files have been optimized.
  4081. //
  4082. Size = sizeof(BootFilesWereOptimized);
  4083. ErrorCode = RegQueryValueEx(PfSvcGlobals.ServiceDataKey,
  4084. PFSVC_BOOT_FILES_OPTIMIZED_VALUE_NAME,
  4085. NULL,
  4086. &RegValueType,
  4087. (PVOID) &BootFilesWereOptimized,
  4088. &Size);
  4089. if (ErrorCode != ERROR_SUCCESS) {
  4090. BootFilesWereOptimized = FALSE;
  4091. }
  4092. //
  4093. // Get optimal layout file path.
  4094. //
  4095. ErrorCode = PfSvGetLayoutFilePath(&LayoutFilePath,
  4096. &LayoutFilePathBufferSize);
  4097. if (ErrorCode != ERROR_SUCCESS) {
  4098. goto cleanup;
  4099. }
  4100. //
  4101. // Determine when the file was last modified.
  4102. //
  4103. ErrorCode = PfSvGetLastWriteTime(LayoutFilePath, &LayoutFileTime);
  4104. if (ErrorCode == ERROR_SUCCESS) {
  4105. MissingOriginalLayoutFile = FALSE;
  4106. //
  4107. // If the file was modified after we laid out the files on the disk
  4108. // its contents are not interesting. Otherwise, if the new optimal
  4109. // layout is similar to layout specified in the file, we may not
  4110. // have to re-layout the files.
  4111. //
  4112. if (CompareFileTime(&LayoutFileTime, &LastDiskLayoutTime) <= 0) {
  4113. //
  4114. // Read the current layout.
  4115. //
  4116. ErrorCode = PfSvReadLayout(LayoutFilePath,
  4117. &CurrentLayout,
  4118. &LayoutFileTime);
  4119. if (ErrorCode != ERROR_SUCCESS) {
  4120. //
  4121. // The layout file seems to be bad / inaccesible.
  4122. // Cleanup the path list, so a brand new one gets
  4123. // built.
  4124. //
  4125. PfSvCleanupPathList(&CurrentLayout);
  4126. PfSvInitializePathList(&CurrentLayout, NULL, FALSE);
  4127. }
  4128. }
  4129. } else {
  4130. //
  4131. // We could not get the timestamp on the original layout file.
  4132. // It might have been deleted.
  4133. //
  4134. MissingOriginalLayoutFile = TRUE;
  4135. }
  4136. //
  4137. // Determine what the current optimal layout should be from
  4138. // scenario files.
  4139. //
  4140. ErrorCode = PfSvDetermineOptimalLayout(Task,
  4141. &OptimalLayout,
  4142. &BootScenarioProcessed);
  4143. if (ErrorCode != ERROR_SUCCESS) {
  4144. goto cleanup;
  4145. }
  4146. //
  4147. // Update current layout based on what optimal layout should be.
  4148. // If the two are similar we don't need to launch the defragger.
  4149. //
  4150. ErrorCode = PfSvUpdateLayout(&CurrentLayout,
  4151. &OptimalLayout,
  4152. &LayoutChanged);
  4153. if (ErrorCode == ERROR_SUCCESS) {
  4154. if (!LayoutChanged) {
  4155. ErrorCode = ERROR_SUCCESS;
  4156. goto cleanup;
  4157. }
  4158. //
  4159. // We'll use the updated layout.
  4160. //
  4161. NewLayout = &CurrentLayout;
  4162. } else {
  4163. //
  4164. // We'll run with the optimal layout.
  4165. //
  4166. NewLayout = &OptimalLayout;
  4167. }
  4168. //
  4169. // Optimal way to layout files has changed. Write out the new layout.
  4170. //
  4171. ErrorCode = PfSvSaveLayout(LayoutFilePath,
  4172. NewLayout,
  4173. &LayoutFileTime);
  4174. if (ErrorCode != ERROR_SUCCESS) {
  4175. goto cleanup;
  4176. }
  4177. //
  4178. // If enough time has not passed since last disk layout don't run the
  4179. // defragger again unless...
  4180. //
  4181. CheckForLayoutFrequencyLimit = TRUE;
  4182. //
  4183. // - We've been explicitly asked to update the layout (i.e. no idle
  4184. // task context.)
  4185. //
  4186. if (!Task) {
  4187. CheckForLayoutFrequencyLimit = FALSE;
  4188. }
  4189. //
  4190. // - Someone seems to have deleted the layout file and we recreated it.
  4191. //
  4192. if (MissingOriginalLayoutFile) {
  4193. CheckForLayoutFrequencyLimit = FALSE;
  4194. }
  4195. //
  4196. // - Boot prefetching is enabled but boot files have not been optimized
  4197. // yet and we processed the list of files from the boot this time.
  4198. //
  4199. if (PfSvcGlobals.Parameters.EnableStatus[PfSystemBootScenarioType] == PfSvEnabled) {
  4200. if (!BootFilesWereOptimized && BootScenarioProcessed) {
  4201. CheckForLayoutFrequencyLimit = FALSE;
  4202. }
  4203. }
  4204. if (CheckForLayoutFrequencyLimit) {
  4205. //
  4206. // We will check to see if enough time has passed by getting current
  4207. // time and comparing it to last disk layout time.
  4208. //
  4209. LastDiskLayoutTimeLI.LowPart = LastDiskLayoutTime.dwLowDateTime;
  4210. LastDiskLayoutTimeLI.HighPart = LastDiskLayoutTime.dwHighDateTime;
  4211. //
  4212. // Get current time as file time.
  4213. //
  4214. GetSystemTimeAsFileTime(&CurrentTime);
  4215. CurrentTimeLI.LowPart = CurrentTime.dwLowDateTime;
  4216. CurrentTimeLI.HighPart = CurrentTime.dwHighDateTime;
  4217. //
  4218. // Check to make sure that current time is after last disk layout time
  4219. // (in case the user has played with time.)
  4220. //
  4221. if (CurrentTimeLI.QuadPart > LastDiskLayoutTimeLI.QuadPart) {
  4222. //
  4223. // Query how long has to pass before we re-layout the files on
  4224. // disk.
  4225. //
  4226. Size = sizeof(MinHoursBeforeRelayout);
  4227. ErrorCode = RegQueryValueEx(PfSvcGlobals.ServiceDataKey,
  4228. PFSVC_MIN_RELAYOUT_HOURS_VALUE_NAME,
  4229. NULL,
  4230. &RegValueType,
  4231. (PVOID) &MinHoursBeforeRelayout,
  4232. &Size);
  4233. if (ErrorCode == ERROR_SUCCESS) {
  4234. MinTimeBeforeRelayoutLI.QuadPart = PFSVC_NUM_100NS_IN_AN_HOUR * MinHoursBeforeRelayout;
  4235. } else {
  4236. MinTimeBeforeRelayoutLI.QuadPart = PFSVC_MIN_TIME_BEFORE_DISK_RELAYOUT;
  4237. }
  4238. if (CurrentTimeLI.QuadPart < LastDiskLayoutTimeLI.QuadPart +
  4239. MinTimeBeforeRelayoutLI.QuadPart) {
  4240. //
  4241. // Not enough time has passed before last disk layout.
  4242. //
  4243. ErrorCode = ERROR_INVALID_TIME;
  4244. goto cleanup;
  4245. }
  4246. }
  4247. }
  4248. //
  4249. // Launch the defragger for layout optimization.
  4250. //
  4251. ErrorCode = PfSvLaunchDefragger(Task, TRUE, NULL);
  4252. if (ErrorCode != ERROR_SUCCESS) {
  4253. goto cleanup;
  4254. }
  4255. //
  4256. // Save whether boot files were optimized.
  4257. //
  4258. ErrorCode = RegSetValueEx(PfSvcGlobals.ServiceDataKey,
  4259. PFSVC_BOOT_FILES_OPTIMIZED_VALUE_NAME,
  4260. 0,
  4261. REG_DWORD,
  4262. (PVOID) &BootScenarioProcessed,
  4263. sizeof(BootScenarioProcessed));
  4264. //
  4265. // Save the last time we updated disk layout to the registry.
  4266. //
  4267. ErrorCode = PfSvSetLastDiskLayoutTime(&LayoutFileTime);
  4268. //
  4269. // Fall through with error code.
  4270. //
  4271. cleanup:
  4272. DBGPR((PFID,PFTRC,"PFSVC: UpdateOptimalLayout(%p)=%x\n", Task, ErrorCode));
  4273. PfSvCleanupPathList(&OptimalLayout);
  4274. PfSvCleanupPathList(&CurrentLayout);
  4275. if (LayoutFilePath) {
  4276. PFSVC_FREE(LayoutFilePath);
  4277. }
  4278. return ErrorCode;
  4279. }
  4280. DWORD
  4281. PfSvUpdateLayout (
  4282. PPFSVC_PATH_LIST CurrentLayout,
  4283. PPFSVC_PATH_LIST OptimalLayout,
  4284. PBOOLEAN LayoutChanged
  4285. )
  4286. /*++
  4287. Routine Description:
  4288. This routine updates the specified layout based on the new optimal
  4289. layout. If the two layouts are similar, CurrentLayout is not updated.
  4290. An error may be returned while CurrentLayout is being updated. It is the
  4291. caller's responsibility to revert CurrentLayout to its original in that case.
  4292. Arguments:
  4293. CurrentLayout - Current file layout.
  4294. OptimalLayout - Newly determined optimal file layout.
  4295. LayoutChanged - Whether Layout was changed.
  4296. Return Value:
  4297. Win32 error code.
  4298. --*/
  4299. {
  4300. DWORD ErrorCode;
  4301. PPFSVC_PATH PathEntry;
  4302. ULONG NumOptimalLayoutFiles;
  4303. ULONG NumMissingFiles;
  4304. ULONG NumCommonFiles;
  4305. ULONG NumCurrentLayoutOnlyFiles;
  4306. //
  4307. // Initialize locals.
  4308. //
  4309. NumOptimalLayoutFiles = 0;
  4310. NumMissingFiles = 0;
  4311. //
  4312. // Go through the paths in the new layout counting the differences with
  4313. // the current layout.
  4314. //
  4315. PathEntry = NULL;
  4316. while (PathEntry = PfSvGetNextPathInOrder(OptimalLayout, PathEntry)) {
  4317. NumOptimalLayoutFiles++;
  4318. if (!PfSvIsInPathList(CurrentLayout, PathEntry->Path, PathEntry->Length)) {
  4319. NumMissingFiles++;
  4320. }
  4321. }
  4322. //
  4323. // Make some sanity checks about the statistics gathered.
  4324. //
  4325. PFSVC_ASSERT(NumOptimalLayoutFiles == OptimalLayout->NumPaths);
  4326. PFSVC_ASSERT(NumOptimalLayoutFiles >= NumMissingFiles);
  4327. NumCommonFiles = NumOptimalLayoutFiles - NumMissingFiles;
  4328. PFSVC_ASSERT(CurrentLayout->NumPaths >= NumCommonFiles);
  4329. NumCurrentLayoutOnlyFiles = CurrentLayout->NumPaths - NumCommonFiles;
  4330. //
  4331. // If there are not that many new files: no need to update the layout.
  4332. //
  4333. if (NumMissingFiles <= 20) {
  4334. *LayoutChanged = FALSE;
  4335. ErrorCode = ERROR_SUCCESS;
  4336. goto cleanup;
  4337. }
  4338. //
  4339. // We will be updating the current layout.
  4340. //
  4341. *LayoutChanged = TRUE;
  4342. //
  4343. // If there are too many files in the current layout that don't need to be
  4344. // there anymore, rebuild the list.
  4345. //
  4346. if (NumCurrentLayoutOnlyFiles >= CurrentLayout->NumPaths / 4) {
  4347. PfSvCleanupPathList(CurrentLayout);
  4348. PfSvInitializePathList(CurrentLayout, NULL, FALSE);
  4349. }
  4350. //
  4351. // Add files from the optimal layout to the end of current layout.
  4352. //
  4353. while (PathEntry = PfSvGetNextPathInOrder(OptimalLayout, PathEntry)) {
  4354. ErrorCode = PfSvAddToPathList(CurrentLayout, PathEntry->Path, PathEntry->Length);
  4355. if (ErrorCode != ERROR_SUCCESS) {
  4356. goto cleanup;
  4357. }
  4358. }
  4359. ErrorCode = ERROR_SUCCESS;
  4360. cleanup:
  4361. DBGPR((PFID,PFTRC,"PFSVC: UpdateLayout(%p,%p)=%d,%x\n",CurrentLayout,OptimalLayout,*LayoutChanged,ErrorCode));
  4362. return ErrorCode;
  4363. }
  4364. DWORD
  4365. PfSvDetermineOptimalLayout (
  4366. PPFSVC_IDLE_TASK Task,
  4367. PPFSVC_PATH_LIST OptimalLayout,
  4368. BOOL *BootScenarioProcessed
  4369. )
  4370. /*++
  4371. Routine Description:
  4372. This routine will determine if the optimal disk layout has to be
  4373. updated by looking at the existing scenario files.
  4374. Arguments:
  4375. Task - If specified the function will check Task every once in a
  4376. while to see if it should exit with ERROR_RETRY.
  4377. OptimalLayout - Initialized empty path list that will be built.
  4378. BootScenarioProcessed - Whether we got the list of boot files from
  4379. the boot scenario.
  4380. Return Value:
  4381. Win32 error code.
  4382. --*/
  4383. {
  4384. PFSVC_SCENARIO_FILE_CURSOR FileCursor;
  4385. FILETIME LayoutFileTime;
  4386. PNTPATH_TRANSLATION_LIST TranslationList;
  4387. PWCHAR DosPathBuffer;
  4388. ULONG DosPathBufferSize;
  4389. DWORD ErrorCode;
  4390. BOOLEAN AcquiredLock;
  4391. WCHAR BootScenarioFileName[PF_MAX_SCENARIO_FILE_NAME];
  4392. WCHAR BootScenarioFilePath[MAX_PATH + 1];
  4393. //
  4394. // Initialize locals.
  4395. //
  4396. PfSvInitializeScenarioFileCursor(&FileCursor);
  4397. TranslationList = NULL;
  4398. AcquiredLock = FALSE;
  4399. DosPathBuffer = NULL;
  4400. DosPathBufferSize = 0;
  4401. DBGPR((PFID,PFTRC,"PFSVC: DetermineOptimalLayout(%p,%p)\n",Task,OptimalLayout));
  4402. //
  4403. // Initialize output variables.
  4404. //
  4405. *BootScenarioProcessed = FALSE;
  4406. //
  4407. // Acquire the prefetch root directory lock and initialize some locals.
  4408. //
  4409. PFSVC_ACQUIRE_LOCK(PfSvcGlobals.PrefetchRootLock);
  4410. AcquiredLock = TRUE;
  4411. //
  4412. // Start the file cursor.
  4413. //
  4414. ErrorCode = PfSvStartScenarioFileCursor(&FileCursor, PfSvcGlobals.PrefetchRoot);
  4415. if (ErrorCode != ERROR_SUCCESS) {
  4416. goto cleanup;
  4417. }
  4418. //
  4419. // Build the boot scenario file path.
  4420. //
  4421. swprintf(BootScenarioFileName,
  4422. PF_SCEN_FILE_NAME_FORMAT,
  4423. PF_BOOT_SCENARIO_NAME,
  4424. PF_BOOT_SCENARIO_HASHID,
  4425. PF_PREFETCH_FILE_EXTENSION);
  4426. _snwprintf(BootScenarioFilePath,
  4427. MAX_PATH,
  4428. L"%ws\\%ws",
  4429. PfSvcGlobals.PrefetchRoot,
  4430. BootScenarioFileName);
  4431. BootScenarioFilePath[MAX_PATH - 1] = 0;
  4432. PFSVC_RELEASE_LOCK(PfSvcGlobals.PrefetchRootLock);
  4433. AcquiredLock = FALSE;
  4434. //
  4435. // Get translation list so we can convert NT paths in the trace to
  4436. // Dos paths that the defragger understands.
  4437. //
  4438. ErrorCode = PfSvBuildNtPathTranslationList(&TranslationList);
  4439. if (ErrorCode != ERROR_SUCCESS) {
  4440. goto cleanup;
  4441. }
  4442. //
  4443. // Should we continue to run?
  4444. //
  4445. ErrorCode = PfSvContinueRunningTask(Task);
  4446. if (ErrorCode != ERROR_SUCCESS) {
  4447. goto cleanup;
  4448. }
  4449. //
  4450. // Add boot loader files to optimal layout.
  4451. //
  4452. ErrorCode = PfSvBuildBootLoaderFilesList(OptimalLayout);
  4453. if (ErrorCode != ERROR_SUCCESS) {
  4454. goto cleanup;
  4455. }
  4456. //
  4457. // Add files from the boot scenario.
  4458. //
  4459. ErrorCode = PfSvUpdateLayoutForScenario(OptimalLayout,
  4460. BootScenarioFilePath,
  4461. TranslationList,
  4462. &DosPathBuffer,
  4463. &DosPathBufferSize);
  4464. if (ErrorCode == ERROR_SUCCESS) {
  4465. *BootScenarioProcessed = TRUE;
  4466. }
  4467. //
  4468. // Go through all the other scenario files.
  4469. //
  4470. while (TRUE) {
  4471. //
  4472. // Should we continue to run?
  4473. //
  4474. ErrorCode = PfSvContinueRunningTask(Task);
  4475. if (ErrorCode != ERROR_SUCCESS) {
  4476. goto cleanup;
  4477. }
  4478. //
  4479. // Get file info for the next scenario file.
  4480. //
  4481. ErrorCode = PfSvGetNextScenarioFileInfo(&FileCursor);
  4482. if (ErrorCode == ERROR_NO_MORE_FILES) {
  4483. break;
  4484. }
  4485. if (ErrorCode != ERROR_SUCCESS) {
  4486. goto cleanup;
  4487. }
  4488. PfSvUpdateLayoutForScenario(OptimalLayout,
  4489. FileCursor.FilePath,
  4490. TranslationList,
  4491. &DosPathBuffer,
  4492. &DosPathBufferSize);
  4493. }
  4494. //
  4495. // We are done.
  4496. //
  4497. ErrorCode = ERROR_SUCCESS;
  4498. cleanup:
  4499. DBGPR((PFID,PFTRC,"PFSVC: DetermineOptimalLayout(%p,%p)=%x\n",Task,OptimalLayout,ErrorCode));
  4500. if (AcquiredLock) {
  4501. PFSVC_RELEASE_LOCK(PfSvcGlobals.PrefetchRootLock);
  4502. }
  4503. PfSvCleanupScenarioFileCursor(&FileCursor);
  4504. if (TranslationList) {
  4505. PfSvFreeNtPathTranslationList(TranslationList);
  4506. }
  4507. if (DosPathBuffer) {
  4508. PFSVC_ASSERT(DosPathBufferSize);
  4509. PFSVC_FREE(DosPathBuffer);
  4510. }
  4511. return ErrorCode;
  4512. }
  4513. DWORD
  4514. PfSvUpdateLayoutForScenario (
  4515. PPFSVC_PATH_LIST OptimalLayout,
  4516. WCHAR *ScenarioFilePath,
  4517. PNTPATH_TRANSLATION_LIST TranslationList,
  4518. PWCHAR *DosPathBuffer,
  4519. PULONG DosPathBufferSize
  4520. )
  4521. /*++
  4522. Routine Description:
  4523. This routine will add the directories and files referenced in a
  4524. scenario in the order they appear to the specified optimal layout
  4525. path list .
  4526. Arguments:
  4527. OptimalLayout - Pointer to path list.
  4528. ScenarioFilePath - Scenario file.
  4529. TranslationList, DosPathBuffer, DosPathBufferSize - These are used
  4530. to translate NT path names in the scenario file to Dos path names
  4531. that should be in the layout file.
  4532. Return Value:
  4533. Win32 error code.
  4534. --*/
  4535. {
  4536. PPF_SCENARIO_HEADER Scenario;
  4537. PCHAR MetadataInfoBase;
  4538. PPF_METADATA_RECORD MetadataRecordTable;
  4539. PPF_METADATA_RECORD MetadataRecord;
  4540. PPF_COUNTED_STRING DirectoryPath;
  4541. PPF_SECTION_RECORD Sections;
  4542. PCHAR FilePathInfo;
  4543. PWCHAR FilePath;
  4544. ULONG FilePathLength;
  4545. ULONG SectionIdx;
  4546. ULONG MetadataRecordIdx;
  4547. ULONG DirectoryIdx;
  4548. DWORD ErrorCode;
  4549. DWORD FileSize;
  4550. DWORD FailedCheck;
  4551. //
  4552. // Initialize locals.
  4553. //
  4554. Scenario = NULL;
  4555. //
  4556. // Map the scenario file.
  4557. //
  4558. ErrorCode = PfSvGetViewOfFile(ScenarioFilePath,
  4559. &Scenario,
  4560. &FileSize);
  4561. if (ErrorCode != ERROR_SUCCESS) {
  4562. goto cleanup;
  4563. }
  4564. //
  4565. // Verify scenario file.
  4566. //
  4567. if (!PfSvVerifyScenarioBuffer(Scenario, FileSize, &FailedCheck) ||
  4568. Scenario->ServiceVersion != PFSVC_SERVICE_VERSION) {
  4569. ErrorCode = ERROR_BAD_FORMAT;
  4570. goto cleanup;
  4571. }
  4572. //
  4573. // First add the directories that need to be accessed.
  4574. //
  4575. MetadataInfoBase = (PCHAR)Scenario + Scenario->MetadataInfoOffset;
  4576. MetadataRecordTable = (PPF_METADATA_RECORD) MetadataInfoBase;
  4577. for (MetadataRecordIdx = 0;
  4578. MetadataRecordIdx < Scenario->NumMetadataRecords;
  4579. MetadataRecordIdx++) {
  4580. MetadataRecord = &MetadataRecordTable[MetadataRecordIdx];
  4581. DirectoryPath = (PPF_COUNTED_STRING)
  4582. (MetadataInfoBase + MetadataRecord->DirectoryPathsOffset);
  4583. for (DirectoryIdx = 0;
  4584. DirectoryIdx < MetadataRecord->NumDirectories;
  4585. DirectoryIdx++,
  4586. DirectoryPath = (PPF_COUNTED_STRING) (&DirectoryPath->String[DirectoryPath->Length + 1])) {
  4587. ErrorCode = PfSvTranslateNtPath(TranslationList,
  4588. DirectoryPath->String,
  4589. DirectoryPath->Length,
  4590. DosPathBuffer,
  4591. DosPathBufferSize);
  4592. //
  4593. // We may not be able to translate all NT paths to Dos paths.
  4594. //
  4595. if (ErrorCode == ERROR_SUCCESS) {
  4596. ErrorCode = PfSvAddToPathList(OptimalLayout,
  4597. *DosPathBuffer,
  4598. wcslen(*DosPathBuffer));
  4599. if (ErrorCode != ERROR_SUCCESS) {
  4600. goto cleanup;
  4601. }
  4602. }
  4603. }
  4604. }
  4605. //
  4606. // Now add the file paths.
  4607. //
  4608. Sections = (PPF_SECTION_RECORD) ((PCHAR)Scenario + Scenario->SectionInfoOffset);
  4609. FilePathInfo = (PCHAR)Scenario + Scenario->FileNameInfoOffset;
  4610. for (SectionIdx = 0; SectionIdx < Scenario->NumSections; SectionIdx++) {
  4611. FilePath = (PWSTR) (FilePathInfo + Sections[SectionIdx].FileNameOffset);
  4612. FilePathLength = Sections[SectionIdx].FileNameLength;
  4613. ErrorCode = PfSvTranslateNtPath(TranslationList,
  4614. FilePath,
  4615. FilePathLength,
  4616. DosPathBuffer,
  4617. DosPathBufferSize);
  4618. //
  4619. // We may not be able to translate all NT paths to Dos paths.
  4620. //
  4621. if (ErrorCode == ERROR_SUCCESS) {
  4622. ErrorCode = PfSvAddToPathList(OptimalLayout,
  4623. *DosPathBuffer,
  4624. wcslen(*DosPathBuffer));
  4625. if (ErrorCode != ERROR_SUCCESS) {
  4626. goto cleanup;
  4627. }
  4628. }
  4629. }
  4630. //
  4631. // We are done.
  4632. //
  4633. ErrorCode = ERROR_SUCCESS;
  4634. cleanup:
  4635. if (Scenario) {
  4636. UnmapViewOfFile(Scenario);
  4637. }
  4638. DBGPR((PFID,PFTRC,"PFSVC: UpdateLayoutForScenario(%p,%ws)=%x\n",OptimalLayout,ScenarioFilePath,ErrorCode));
  4639. return ErrorCode;
  4640. }
  4641. DWORD
  4642. PfSvReadLayout(
  4643. IN WCHAR *FilePath,
  4644. OUT PPFSVC_PATH_LIST Layout,
  4645. OUT FILETIME *LastWriteTime
  4646. )
  4647. /*++
  4648. Routine Description:
  4649. This function adds contents of the optimal layout file to the
  4650. specified path list. Note that failure may be returned after
  4651. adding several files to the list.
  4652. Arguments:
  4653. FilePath - NUL terminated path to optimal layout file.
  4654. Layout - Pointer to initialized path list.
  4655. LastWriteTime - Last write time of the read file.
  4656. Return Value:
  4657. Win32 error code.
  4658. --*/
  4659. {
  4660. DWORD ErrorCode;
  4661. FILE *LayoutFile;
  4662. WCHAR *LineBuffer;
  4663. ULONG LineBufferMaxChars;
  4664. ULONG LineLength;
  4665. //
  4666. // Initialize locals.
  4667. //
  4668. LayoutFile = NULL;
  4669. LineBuffer = NULL;
  4670. LineBufferMaxChars = 0;
  4671. //
  4672. // Open the layout file.
  4673. //
  4674. LayoutFile = _wfopen(FilePath, L"rb");
  4675. if (!LayoutFile) {
  4676. ErrorCode = ERROR_FILE_NOT_FOUND;
  4677. goto cleanup;
  4678. }
  4679. //
  4680. // Read and verify header.
  4681. //
  4682. ErrorCode = PfSvReadLine(LayoutFile,
  4683. &LineBuffer,
  4684. &LineBufferMaxChars,
  4685. &LineLength);
  4686. if (ErrorCode != ERROR_SUCCESS || !LineLength) {
  4687. ErrorCode = ERROR_BAD_FORMAT;
  4688. goto cleanup;
  4689. }
  4690. PfSvRemoveEndOfLineChars(LineBuffer, &LineLength);
  4691. if (wcscmp(LineBuffer, L"[OptimalLayoutFile]")) {
  4692. //
  4693. // Notepad puts a weird first character in the UNICODE text files.
  4694. // Skip the first character and compare again.
  4695. //
  4696. // FUTURE-2002/03/29-ScottMa -- Strictly speaking, that first character
  4697. // is the UNICODE marker character, which must be 0xFFFE if the file
  4698. // was written on the same -endian machine as it is being read, while
  4699. // 0xFEFF would indicate that the file needs to be converted.
  4700. if ((LineLength < 1) ||
  4701. wcscmp(&LineBuffer[1], L"[OptimalLayoutFile]")) {
  4702. ErrorCode = ERROR_BAD_FORMAT;
  4703. goto cleanup;
  4704. }
  4705. }
  4706. //
  4707. // Read and verify version.
  4708. //
  4709. ErrorCode = PfSvReadLine(LayoutFile,
  4710. &LineBuffer,
  4711. &LineBufferMaxChars,
  4712. &LineLength);
  4713. if (ErrorCode != ERROR_SUCCESS || !LineLength) {
  4714. ErrorCode = ERROR_BAD_FORMAT;
  4715. goto cleanup;
  4716. }
  4717. PfSvRemoveEndOfLineChars(LineBuffer, &LineLength);
  4718. if (wcscmp(LineBuffer, L"Version=1")) {
  4719. ErrorCode = ERROR_BAD_FORMAT;
  4720. goto cleanup;
  4721. }
  4722. //
  4723. // Read in file names.
  4724. //
  4725. do {
  4726. ErrorCode = PfSvReadLine(LayoutFile,
  4727. &LineBuffer,
  4728. &LineBufferMaxChars,
  4729. &LineLength);
  4730. if (ErrorCode != ERROR_SUCCESS) {
  4731. goto cleanup;
  4732. }
  4733. if (!LineLength) {
  4734. //
  4735. // We hit end of file.
  4736. //
  4737. break;
  4738. }
  4739. PfSvRemoveEndOfLineChars(LineBuffer, &LineLength);
  4740. //
  4741. // Add it to the list.
  4742. //
  4743. ErrorCode = PfSvAddToPathList(Layout,
  4744. LineBuffer,
  4745. LineLength);
  4746. if (ErrorCode != ERROR_SUCCESS) {
  4747. goto cleanup;
  4748. }
  4749. } while (TRUE);
  4750. //
  4751. // Get the last write time on the file.
  4752. //
  4753. ErrorCode = PfSvGetLastWriteTime(FilePath, LastWriteTime);
  4754. if (ErrorCode != ERROR_SUCCESS) {
  4755. goto cleanup;
  4756. }
  4757. //
  4758. // We are done.
  4759. //
  4760. ErrorCode = ERROR_SUCCESS;
  4761. cleanup:
  4762. if (LayoutFile) {
  4763. fclose(LayoutFile);
  4764. }
  4765. if (LineBuffer) {
  4766. PFSVC_ASSERT(LineBufferMaxChars);
  4767. PFSVC_FREE(LineBuffer);
  4768. }
  4769. return ErrorCode;
  4770. }
  4771. DWORD
  4772. PfSvSaveLayout(
  4773. IN WCHAR *FilePath,
  4774. IN PPFSVC_PATH_LIST Layout,
  4775. OUT FILETIME *LastWriteTime
  4776. )
  4777. /*++
  4778. Routine Description:
  4779. This routine saves the specified file layout list in order to the
  4780. specified file in the right format.
  4781. Arguments:
  4782. FilePath - Path to output layout file.
  4783. Layout - Pointer to layout.
  4784. LastWriteTime - Last write time on the file after we are done
  4785. saving the layout.
  4786. Return Value:
  4787. Win32 error code.
  4788. --*/
  4789. {
  4790. DWORD ErrorCode;
  4791. HANDLE LayoutFile;
  4792. WCHAR *FileHeader;
  4793. ULONG BufferSize;
  4794. ULONG NumBytesWritten;
  4795. PPFSVC_PATH PathEntry;
  4796. WCHAR *NewLine;
  4797. ULONG SizeOfNewLine;
  4798. //
  4799. // Initialize locals.
  4800. //
  4801. LayoutFile = INVALID_HANDLE_VALUE;
  4802. NewLine = L"\r\n";
  4803. SizeOfNewLine = wcslen(NewLine) * sizeof(WCHAR);
  4804. //
  4805. // Open & truncate the layout file. We are also opening with read
  4806. // permissions so we can query the last write time when we are
  4807. // done.
  4808. //
  4809. LayoutFile = CreateFile(FilePath,
  4810. GENERIC_READ | GENERIC_WRITE,
  4811. 0,
  4812. 0,
  4813. CREATE_ALWAYS,
  4814. 0,
  4815. NULL);
  4816. if (LayoutFile == INVALID_HANDLE_VALUE) {
  4817. ErrorCode = GetLastError();
  4818. goto cleanup;
  4819. }
  4820. //
  4821. // Write out the header.
  4822. //
  4823. FileHeader = L"[OptimalLayoutFile]\r\nVersion=1\r\n";
  4824. BufferSize = wcslen(FileHeader) * sizeof(WCHAR);
  4825. if (!WriteFile(LayoutFile,
  4826. FileHeader,
  4827. BufferSize,
  4828. &NumBytesWritten,
  4829. NULL)) {
  4830. ErrorCode = GetLastError();
  4831. goto cleanup;
  4832. }
  4833. PathEntry = NULL;
  4834. while (PathEntry = PfSvGetNextPathInOrder(Layout, PathEntry)) {
  4835. //
  4836. // Write the path.
  4837. //
  4838. BufferSize = PathEntry->Length * sizeof(WCHAR);
  4839. if (!WriteFile(LayoutFile,
  4840. PathEntry->Path,
  4841. BufferSize,
  4842. &NumBytesWritten,
  4843. NULL)) {
  4844. ErrorCode = GetLastError();
  4845. goto cleanup;
  4846. }
  4847. //
  4848. // Write the newline.
  4849. //
  4850. if (!WriteFile(LayoutFile,
  4851. NewLine,
  4852. SizeOfNewLine,
  4853. &NumBytesWritten,
  4854. NULL)) {
  4855. ErrorCode = GetLastError();
  4856. goto cleanup;
  4857. }
  4858. }
  4859. //
  4860. // Make sure everything is written to the file so our
  4861. // LastWriteTime will be accurate.
  4862. //
  4863. if (!FlushFileBuffers(LayoutFile)) {
  4864. ErrorCode = GetLastError();
  4865. goto cleanup;
  4866. }
  4867. //
  4868. // Get the last write time.
  4869. //
  4870. if (!GetFileTime(LayoutFile, NULL, NULL, LastWriteTime)) {
  4871. ErrorCode = GetLastError();
  4872. goto cleanup;
  4873. }
  4874. //
  4875. // We are done.
  4876. //
  4877. ErrorCode = ERROR_SUCCESS;
  4878. cleanup:
  4879. if (LayoutFile != INVALID_HANDLE_VALUE) {
  4880. CloseHandle(LayoutFile);
  4881. }
  4882. return ErrorCode;
  4883. }
  4884. DWORD
  4885. PfSvGetLayoutFilePath(
  4886. PWCHAR *FilePathBuffer,
  4887. PULONG FilePathBufferSize
  4888. )
  4889. /*++
  4890. Routine Description:
  4891. This function tries to query the layout file path into the
  4892. specified buffer. If the buffer is too small, or NULL, it is
  4893. reallocated. If not NULL, the buffer should have been allocated by
  4894. PFSVC_ALLOC. It is the callers responsibility to free the returned
  4895. buffer using PFSVC_FREE.
  4896. In order to avoid having somebody cause us to overwrite any file
  4897. in the system always the default layout file path is saved in the
  4898. registry and returned.
  4899. Arguments:
  4900. FilePathBuffer - Layout file path will be put into this buffer
  4901. after it is reallocated if it is NULL or not big enough.
  4902. FilePathBufferSize - Maximum size of *FilePathBuffer in bytes.
  4903. Return Value:
  4904. Win32 error code.
  4905. --*/
  4906. {
  4907. ULONG DefaultPathSize;
  4908. ULONG DefaultPathLength;
  4909. HKEY DefragParametersKey;
  4910. DWORD ErrorCode;
  4911. BOOLEAN AcquiredPrefetchRootLock;
  4912. BOOLEAN OpenedParametersKey;
  4913. //
  4914. // Initialize locals.
  4915. //
  4916. AcquiredPrefetchRootLock = FALSE;
  4917. OpenedParametersKey = FALSE;
  4918. //
  4919. // Verify parameters.
  4920. //
  4921. if (*FilePathBufferSize) {
  4922. PFSVC_ASSERT(*FilePathBuffer);
  4923. }
  4924. PFSVC_ACQUIRE_LOCK(PfSvcGlobals.PrefetchRootLock);
  4925. AcquiredPrefetchRootLock = TRUE;
  4926. DefaultPathLength = wcslen(PfSvcGlobals.PrefetchRoot);
  4927. DefaultPathLength += 1; // for '\\'
  4928. DefaultPathLength += wcslen(PFSVC_OPTIMAL_LAYOUT_FILE_DEFAULT_NAME);
  4929. DefaultPathSize = (DefaultPathLength + 1) * sizeof(WCHAR);
  4930. //
  4931. // Check if we have to allocate/reallocate the buffer.
  4932. //
  4933. if ((*FilePathBufferSize) < DefaultPathSize) {
  4934. if (*FilePathBuffer) {
  4935. PFSVC_ASSERT(*FilePathBufferSize);
  4936. PFSVC_FREE(*FilePathBuffer);
  4937. }
  4938. (*FilePathBufferSize) = 0;
  4939. (*FilePathBuffer) = PFSVC_ALLOC(DefaultPathSize);
  4940. if (!(*FilePathBuffer)) {
  4941. ErrorCode = ERROR_NOT_ENOUGH_MEMORY;
  4942. goto cleanup;
  4943. }
  4944. (*FilePathBufferSize) = DefaultPathSize;
  4945. }
  4946. //
  4947. // Build the path in the FilePathBuffer
  4948. //
  4949. wcscpy((*FilePathBuffer), PfSvcGlobals.PrefetchRoot);
  4950. wcscat((*FilePathBuffer), L"\\");
  4951. wcscat((*FilePathBuffer), PFSVC_OPTIMAL_LAYOUT_FILE_DEFAULT_NAME);
  4952. PFSVC_RELEASE_LOCK(PfSvcGlobals.PrefetchRootLock);
  4953. AcquiredPrefetchRootLock = FALSE;
  4954. //
  4955. // Save the default path in the registry so it is used by the
  4956. // defragger:
  4957. //
  4958. //
  4959. // Open the parameters key, creating it if necessary.
  4960. //
  4961. ErrorCode = RegCreateKey(HKEY_LOCAL_MACHINE,
  4962. PFSVC_OPTIMAL_LAYOUT_REG_KEY_PATH,
  4963. &DefragParametersKey);
  4964. if (ErrorCode != ERROR_SUCCESS) {
  4965. goto cleanup;
  4966. }
  4967. OpenedParametersKey = TRUE;
  4968. ErrorCode = RegSetValueEx(DefragParametersKey,
  4969. PFSVC_OPTIMAL_LAYOUT_REG_VALUE_NAME,
  4970. 0,
  4971. REG_SZ,
  4972. (PVOID) (*FilePathBuffer),
  4973. (*FilePathBufferSize));
  4974. if (ErrorCode != ERROR_SUCCESS) {
  4975. goto cleanup;
  4976. }
  4977. cleanup:
  4978. if (AcquiredPrefetchRootLock) {
  4979. PFSVC_RELEASE_LOCK(PfSvcGlobals.PrefetchRootLock);
  4980. }
  4981. if (OpenedParametersKey) {
  4982. CloseHandle(DefragParametersKey);
  4983. }
  4984. return ErrorCode;
  4985. }
  4986. //
  4987. // Routines to defrag the disks once after setup when the system is idle.
  4988. //
  4989. DWORD
  4990. PfSvLaunchDefragger(
  4991. PPFSVC_IDLE_TASK Task,
  4992. BOOLEAN ForLayoutOptimization,
  4993. PWCHAR TargetDrive
  4994. )
  4995. /*++
  4996. Routine Description:
  4997. This routine will launch the defragger. It will create an event that
  4998. will be passed to the defragger so the defragger can be stopped if
  4999. the service is stopping or the Task (if one is specified) is being
  5000. unregistered, etc.
  5001. Arguments:
  5002. Task - If specified the function will check Task every once in a
  5003. while to see if it should exit with ERROR_RETRY.
  5004. ForLayoutOptimization - Whether we are launching the defragger
  5005. only for layout optimization.
  5006. TargetDrive - If we are not launching for layout optimization, the
  5007. drive that we want to defrag.
  5008. Return Value:
  5009. Win32 error code.
  5010. --*/
  5011. {
  5012. PROCESS_INFORMATION ProcessInfo;
  5013. STARTUPINFO StartupInfo;
  5014. WCHAR *CommandLine;
  5015. WCHAR *DefragCommand;
  5016. WCHAR *DoLayoutParameter;
  5017. WCHAR *DriveToDefrag;
  5018. HANDLE StopDefraggerEvent;
  5019. HANDLE ProcessHandle;
  5020. HANDLE Events[4];
  5021. ULONG NumEvents;
  5022. ULONG MaxEvents;
  5023. DWORD ErrorCode;
  5024. DWORD ExitCode;
  5025. DWORD WaitResult;
  5026. DWORD ProcessId;
  5027. ULONG SystemDirLength;
  5028. ULONG CommandLineLength;
  5029. ULONG RetryCount;
  5030. BOOL DefraggerExitOnItsOwn;
  5031. WCHAR SystemDrive[3];
  5032. WCHAR ProcessIdString[35];
  5033. WCHAR StopEventString[35];
  5034. WCHAR SystemDir[MAX_PATH + 1];
  5035. //
  5036. // Initialize locals.
  5037. //
  5038. StopDefraggerEvent = NULL;
  5039. DefragCommand = L"\\defrag.exe\" ";
  5040. DoLayoutParameter = L"-b ";
  5041. CommandLine = NULL;
  5042. RtlZeroMemory(&ProcessInfo, sizeof(PROCESS_INFORMATION));
  5043. RtlZeroMemory(&StartupInfo, sizeof(STARTUPINFO));
  5044. StartupInfo.cb = sizeof(STARTUPINFO);
  5045. ProcessHandle = NULL;
  5046. MaxEvents = sizeof(Events) / sizeof(HANDLE);
  5047. DBGPR((PFID,PFTRC,"PFSVC: LaunchDefragger(%p,%d,%ws)\n",Task,(DWORD)ForLayoutOptimization,TargetDrive));
  5048. //
  5049. // If we are not allowed to run the defragger, don't.
  5050. //
  5051. if (!PfSvAllowedToRunDefragger(TRUE)) {
  5052. ErrorCode = ERROR_ACCESS_DENIED;
  5053. goto cleanup;
  5054. }
  5055. //
  5056. // Get current process ID as string.
  5057. //
  5058. ProcessId = GetCurrentProcessId();
  5059. swprintf(ProcessIdString, L"-p %x ", ProcessId);
  5060. //
  5061. // Create a stop event and convert handle value to string.
  5062. //
  5063. StopDefraggerEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  5064. if (!StopDefraggerEvent) {
  5065. ErrorCode = GetLastError();
  5066. goto cleanup;
  5067. }
  5068. swprintf(StopEventString, L"-s %p ", StopDefraggerEvent);
  5069. //
  5070. // Get path to system32 directory.
  5071. //
  5072. SystemDirLength = GetSystemDirectory(SystemDir, MAX_PATH);
  5073. if (!SystemDirLength) {
  5074. ErrorCode = GetLastError();
  5075. goto cleanup;
  5076. }
  5077. if (SystemDirLength >= MAX_PATH) {
  5078. ErrorCode = ERROR_INSUFFICIENT_BUFFER;
  5079. goto cleanup;
  5080. }
  5081. SystemDir[MAX_PATH - 1] = 0;
  5082. //
  5083. // Determine which drive we will be defragmenting.
  5084. //
  5085. if (ForLayoutOptimization) {
  5086. //
  5087. // Get system drive from system directory path.
  5088. //
  5089. SystemDrive[0] = SystemDir[0];
  5090. SystemDrive[1] = SystemDir[1];
  5091. SystemDrive[2] = 0;
  5092. DriveToDefrag = SystemDrive;
  5093. } else {
  5094. DriveToDefrag = TargetDrive;
  5095. }
  5096. //
  5097. // Build the command line to launch the process. All strings we put
  5098. // together include a trailing space.
  5099. //
  5100. CommandLineLength = 0;
  5101. CommandLineLength += wcslen(L"\""); // protect against spaces in SystemDir.
  5102. CommandLineLength += wcslen(SystemDir);
  5103. CommandLineLength += wcslen(DefragCommand);
  5104. CommandLineLength += wcslen(ProcessIdString);
  5105. CommandLineLength += wcslen(StopEventString);
  5106. if (ForLayoutOptimization) {
  5107. CommandLineLength += wcslen(DoLayoutParameter);
  5108. }
  5109. CommandLineLength += wcslen(DriveToDefrag);
  5110. CommandLine = PFSVC_ALLOC((CommandLineLength + 1) * sizeof(WCHAR));
  5111. if (!CommandLine) {
  5112. ErrorCode = ERROR_NOT_ENOUGH_MEMORY;
  5113. goto cleanup;
  5114. }
  5115. wcscpy(CommandLine, L"\"");
  5116. wcscat(CommandLine, SystemDir);
  5117. wcscat(CommandLine, DefragCommand);
  5118. wcscat(CommandLine, ProcessIdString);
  5119. wcscat(CommandLine, StopEventString);
  5120. if (ForLayoutOptimization) {
  5121. wcscat(CommandLine, DoLayoutParameter);
  5122. }
  5123. wcscat(CommandLine, DriveToDefrag);
  5124. //
  5125. // We may have to launch the defragger multiple times for it to make
  5126. // or determine the space in which to layout files etc.
  5127. //
  5128. for (RetryCount = 0; RetryCount < 20; RetryCount++) {
  5129. PFSVC_ASSERT(!ProcessHandle);
  5130. //
  5131. // Create the process.
  5132. //
  5133. // FUTURE-2002/03/29-ScottMa -- CreateProcess is safer if you supply
  5134. // the first parameter. Since the full command-line was built up
  5135. // by this function, the first parameter is readily available.
  5136. if (!CreateProcess (NULL,
  5137. CommandLine,
  5138. NULL,
  5139. NULL,
  5140. FALSE,
  5141. CREATE_NO_WINDOW,
  5142. NULL,
  5143. NULL,
  5144. &StartupInfo,
  5145. &ProcessInfo)) {
  5146. ErrorCode = GetLastError();
  5147. goto cleanup;
  5148. }
  5149. //
  5150. // Close handle to the thread, save the process handle.
  5151. //
  5152. CloseHandle(ProcessInfo.hThread);
  5153. ProcessHandle = ProcessInfo.hProcess;
  5154. //
  5155. // Setup the events we will wait on.
  5156. //
  5157. NumEvents = 0;
  5158. Events[NumEvents] = ProcessHandle;
  5159. NumEvents++;
  5160. Events[NumEvents] = PfSvcGlobals.TerminateServiceEvent;
  5161. NumEvents ++;
  5162. if (Task) {
  5163. Events[NumEvents] = Task->StartedUnregisteringEvent;
  5164. NumEvents++;
  5165. Events[NumEvents] = Task->StopEvent;
  5166. NumEvents++;
  5167. }
  5168. PFSVC_ASSERT(NumEvents <= MaxEvents);
  5169. DefraggerExitOnItsOwn = FALSE;
  5170. WaitResult = WaitForMultipleObjects(NumEvents,
  5171. Events,
  5172. FALSE,
  5173. INFINITE);
  5174. switch(WaitResult) {
  5175. case WAIT_OBJECT_0:
  5176. //
  5177. // The defragger process exit.
  5178. //
  5179. DefraggerExitOnItsOwn = TRUE;
  5180. break;
  5181. case WAIT_OBJECT_0 + 1:
  5182. //
  5183. // The service is exiting, Signal the defragger to exit, but don't
  5184. // wait for it.
  5185. //
  5186. SetEvent(StopDefraggerEvent);
  5187. ErrorCode = ERROR_SHUTDOWN_IN_PROGRESS;
  5188. goto cleanup;
  5189. break;
  5190. case WAIT_OBJECT_0 + 2:
  5191. case WAIT_OBJECT_0 + 3:
  5192. //
  5193. // We would have specified these wait events only if a Task was
  5194. // specified.
  5195. //
  5196. PFSVC_ASSERT(Task);
  5197. //
  5198. // Signal the defragger process to exit and wait for it to exit.
  5199. //
  5200. SetEvent(StopDefraggerEvent);
  5201. NumEvents = 0;
  5202. Events[NumEvents] = ProcessHandle;
  5203. NumEvents++;
  5204. Events[NumEvents] = PfSvcGlobals.TerminateServiceEvent;
  5205. NumEvents++;
  5206. WaitResult = WaitForMultipleObjects(NumEvents,
  5207. Events,
  5208. FALSE,
  5209. INFINITE);
  5210. if (WaitResult == WAIT_OBJECT_0) {
  5211. //
  5212. // Defragger exit,
  5213. //
  5214. break;
  5215. } else if (WaitResult == WAIT_OBJECT_0 + 1) {
  5216. //
  5217. // Service exiting, cannot wait for the defragger anymore.
  5218. //
  5219. ErrorCode = ERROR_SHUTDOWN_IN_PROGRESS;
  5220. goto cleanup;
  5221. } else {
  5222. ErrorCode = GetLastError();
  5223. goto cleanup;
  5224. }
  5225. break;
  5226. default:
  5227. ErrorCode = GetLastError();
  5228. goto cleanup;
  5229. }
  5230. //
  5231. // If we came here, the defragger exit. Determine its exit code and
  5232. // propagate it. If the defragger exit because we told it to, this should
  5233. // be ENGERR_RETRY.
  5234. //
  5235. if (!GetExitCodeProcess(ProcessHandle, &ExitCode)) {
  5236. ErrorCode = GetLastError();
  5237. goto cleanup;
  5238. }
  5239. //
  5240. // If the defragger needs us to launch it again do so.
  5241. //
  5242. if (DefraggerExitOnItsOwn && (ExitCode == 9)) { // ENGERR_RETRY
  5243. //
  5244. // Reset the event that tells the defragger to stop.
  5245. //
  5246. ResetEvent(StopDefraggerEvent);
  5247. //
  5248. // Close to handle to the old defragger process.
  5249. //
  5250. CloseHandle(ProcessHandle);
  5251. ProcessHandle = NULL;
  5252. //
  5253. // Setup the error code to return. If we've already retried
  5254. // too many times, this is the error that we'll return when
  5255. // we end the retry loop.
  5256. //
  5257. ErrorCode = ERROR_REQUEST_ABORTED;
  5258. continue;
  5259. }
  5260. //
  5261. // If the defragger is crashing, note it so we don't attempt to run
  5262. // it again. When the defragger crashes, its exit code is an NT status
  5263. // code that will be error, e.g. 0xC0000005 for AV etc.
  5264. //
  5265. if (NT_ERROR(ExitCode)) {
  5266. PfSvcGlobals.DefraggerErrorCode = ExitCode;
  5267. }
  5268. //
  5269. // Translate the return value of the defragger to a Win32 error code.
  5270. // These codes are defined in base\fs\utils\dfrg\inc\dfrgcmn.h.
  5271. // I wish they were in a file I could include.
  5272. //
  5273. switch(ExitCode) {
  5274. case 0: ErrorCode = ERROR_SUCCESS; break; // ENG_NOERR
  5275. case 1: ErrorCode = ERROR_RETRY; break; // ENG_USER_CANCELLED
  5276. case 2: ErrorCode = ERROR_INVALID_PARAMETER; break; // ENGERR_BAD_PARAM
  5277. //
  5278. // If the defragger's children processes AV / die, it will return
  5279. // ENGERR_UNKNOWN == 3.
  5280. //
  5281. case 3:
  5282. ErrorCode = ERROR_INVALID_FUNCTION;
  5283. PfSvcGlobals.DefraggerErrorCode = STATUS_UNSUCCESSFUL;
  5284. break;
  5285. case 4: ErrorCode = ERROR_NOT_ENOUGH_MEMORY; break; // ENGERR_NOMEM
  5286. case 7: ErrorCode = ERROR_DISK_FULL; break; // ENGERR_LOW_FREESPACE
  5287. //
  5288. // There is no good translation for the other exit codes or we just
  5289. // don't understand them.
  5290. //
  5291. default: ErrorCode = ERROR_INVALID_FUNCTION;
  5292. }
  5293. //
  5294. // The defragger returned success or an error other than retry.
  5295. //
  5296. break;
  5297. }
  5298. //
  5299. // Fall through with error code.
  5300. //
  5301. cleanup:
  5302. DBGPR((PFID,PFTRC,"PFSVC: LaunchDefragger(%p)=%x\n",Task,ErrorCode));
  5303. if (CommandLine) {
  5304. PFSVC_FREE(CommandLine);
  5305. }
  5306. if (StopDefraggerEvent) {
  5307. CloseHandle(StopDefraggerEvent);
  5308. }
  5309. if (ProcessHandle) {
  5310. CloseHandle(ProcessHandle);
  5311. }
  5312. return ErrorCode;
  5313. }
  5314. DWORD
  5315. PfSvGetBuildDefragStatusValueName (
  5316. OSVERSIONINFOEXW *OsVersion,
  5317. PWCHAR *ValueName
  5318. )
  5319. /*++
  5320. Routine Description:
  5321. This routine translates OsVersion to a string allocated with
  5322. PFSVC_ALLOC. The returned string should be freed by caller.
  5323. Arguments:
  5324. OsVersion - Version info to translate to string.
  5325. ValueName - Pointer to output string is returned here.
  5326. Return Value:
  5327. Win32 error code.
  5328. --*/
  5329. {
  5330. PWCHAR BuildName;
  5331. PWCHAR BuildNameFormat;
  5332. ULONG BuildNameMaxLength;
  5333. DWORD ErrorCode;
  5334. //
  5335. // Initialize locals.
  5336. //
  5337. BuildName = NULL;
  5338. BuildNameFormat = L"%x.%x.%x.%hx.%hx.%hx.%hx_DefragStatus";
  5339. BuildNameMaxLength = 80;
  5340. //
  5341. // Allocate the string.
  5342. //
  5343. BuildName = PFSVC_ALLOC((BuildNameMaxLength + 1) * sizeof(WCHAR));
  5344. if (!BuildName) {
  5345. ErrorCode = ERROR_NOT_ENOUGH_MEMORY;
  5346. goto cleanup;
  5347. }
  5348. _snwprintf(BuildName,
  5349. BuildNameMaxLength,
  5350. BuildNameFormat,
  5351. OsVersion->dwBuildNumber,
  5352. OsVersion->dwMajorVersion,
  5353. OsVersion->dwMinorVersion,
  5354. (WORD) OsVersion->wSuiteMask,
  5355. (WORD) OsVersion->wProductType,
  5356. (WORD) OsVersion->wServicePackMajor,
  5357. (WORD) OsVersion->wServicePackMinor);
  5358. //
  5359. // Make sure the string is terminated.
  5360. //
  5361. BuildName[BuildNameMaxLength] = 0;
  5362. *ValueName = BuildName;
  5363. ErrorCode = ERROR_SUCCESS;
  5364. cleanup:
  5365. if (ErrorCode != ERROR_SUCCESS) {
  5366. if (BuildName) {
  5367. PFSVC_FREE(BuildName);
  5368. }
  5369. }
  5370. DBGPR((PFID,PFTRC,"PFSVC: GetBuildName(%.80ws)=%x\n",BuildName,ErrorCode));
  5371. return ErrorCode;
  5372. }
  5373. DWORD
  5374. PfSvSetBuildDefragStatus(
  5375. OSVERSIONINFOEXW *OsVersion,
  5376. PWCHAR BuildDefragStatus,
  5377. ULONG Size
  5378. )
  5379. /*++
  5380. Routine Description:
  5381. This routine will set the information on which drives have been
  5382. defragmented and such for the specified build (OsVersion).
  5383. Defrag status is in REG_MULTI_SZ format. Each element is a drive
  5384. path that has been defragged for this build. If all drives were
  5385. defragged, than the first element is PFSVC_DEFRAG_DRIVES_DONE.
  5386. Arguments:
  5387. OsVersion - Build & SP we are setting defrag status for.
  5388. BuildDefragStatus - A string that describes the status in
  5389. REG_MULTI_SZ format.
  5390. Size - Size in bytes of the data that has to be saved to the registry.
  5391. Return Value:
  5392. Win32 error code.
  5393. --*/
  5394. {
  5395. PWCHAR ValueName;
  5396. DWORD ErrorCode;
  5397. //
  5398. // Initialize locals.
  5399. //
  5400. ValueName = NULL;
  5401. //
  5402. // Build the value name from OS version info.
  5403. //
  5404. ErrorCode = PfSvGetBuildDefragStatusValueName(OsVersion, &ValueName);
  5405. if (ErrorCode != ERROR_SUCCESS) {
  5406. goto cleanup;
  5407. }
  5408. ErrorCode = RegSetValueEx(PfSvcGlobals.ServiceDataKey,
  5409. ValueName,
  5410. 0,
  5411. REG_MULTI_SZ,
  5412. (PVOID) BuildDefragStatus,
  5413. Size);
  5414. //
  5415. // Fall through with error code.
  5416. //
  5417. cleanup:
  5418. DBGPR((PFID,PFTRC,"PFSVC: SetBuildDefragStatus(%ws)=%x\n",BuildDefragStatus,ErrorCode));
  5419. if (ValueName) {
  5420. PFSVC_FREE(ValueName);
  5421. }
  5422. return ErrorCode;
  5423. }
  5424. DWORD
  5425. PfSvGetBuildDefragStatus(
  5426. OSVERSIONINFOEXW *OsVersion,
  5427. PWCHAR *BuildDefragStatus,
  5428. PULONG ReturnSize
  5429. )
  5430. /*++
  5431. Routine Description:
  5432. This routine will get the information on which drives have been
  5433. defragmented and such for the specified build (OsVersion).
  5434. Defrag status is in REG_MULTI_SZ format. Each element is a drive
  5435. path that has been defragged for this build. If all drives were
  5436. defragged, than the first element is PFSVC_DEFRAG_DRIVES_DONE.
  5437. Arguments:
  5438. OsVersion - Build & SP we are querying defrag status for.
  5439. BuildDefragStatus - Output for defrag status. If the function returns
  5440. success this should be freed with a call to PFSVC_FREE().
  5441. ReturnSize - Size of the returned value in bytes.
  5442. Return Value:
  5443. Win32 error code.
  5444. --*/
  5445. {
  5446. PWCHAR ValueBuffer;
  5447. PWCHAR ValueName;
  5448. DWORD ErrorCode;
  5449. DWORD RegValueType;
  5450. ULONG ValueBufferSize;
  5451. ULONG Size;
  5452. ULONG NumTries;
  5453. ULONG DefaultValueSize;
  5454. BOOLEAN InvalidValue;
  5455. //
  5456. // Initialize locals.
  5457. //
  5458. ValueName = NULL;
  5459. ValueBuffer = NULL;
  5460. ValueBufferSize = 0;
  5461. InvalidValue = FALSE;
  5462. //
  5463. // Build the value name from OS version info.
  5464. //
  5465. ErrorCode = PfSvGetBuildDefragStatusValueName(OsVersion, &ValueName);
  5466. if (ErrorCode != ERROR_SUCCESS) {
  5467. goto cleanup;
  5468. }
  5469. //
  5470. // Try to allocate a right size buffer to read this value into.
  5471. //
  5472. NumTries = 0;
  5473. do {
  5474. Size = ValueBufferSize;
  5475. ErrorCode = RegQueryValueEx(PfSvcGlobals.ServiceDataKey,
  5476. ValueName,
  5477. NULL,
  5478. &RegValueType,
  5479. (PVOID) ValueBuffer,
  5480. &Size);
  5481. //
  5482. // API returns SUCCESS with required size in Size if ValueBuffer
  5483. // is NULL. We have to special case that out.
  5484. //
  5485. if (ValueBuffer && ErrorCode == ERROR_SUCCESS) {
  5486. //
  5487. // We got it. Check the type.
  5488. //
  5489. if (RegValueType != REG_MULTI_SZ) {
  5490. //
  5491. // Return default value.
  5492. //
  5493. InvalidValue = TRUE;
  5494. } else {
  5495. InvalidValue = FALSE;
  5496. *ReturnSize = Size;
  5497. }
  5498. break;
  5499. }
  5500. if (ErrorCode == ERROR_FILE_NOT_FOUND) {
  5501. //
  5502. // The value does not exist. Return default value.
  5503. //
  5504. InvalidValue = TRUE;
  5505. break;
  5506. }
  5507. if (ErrorCode != ERROR_MORE_DATA &&
  5508. !(ErrorCode == ERROR_SUCCESS && !ValueBuffer)) {
  5509. //
  5510. // This is a real error.
  5511. //
  5512. goto cleanup;
  5513. }
  5514. //
  5515. // Allocate a bigger buffer and try again.
  5516. //
  5517. PFSVC_ASSERT(ValueBufferSize < Size);
  5518. if (ValueBuffer) {
  5519. PFSVC_ASSERT(ValueBufferSize);
  5520. PFSVC_FREE(ValueBuffer);
  5521. ValueBufferSize = 0;
  5522. }
  5523. ValueBuffer = PFSVC_ALLOC(Size);
  5524. if (!ValueBuffer) {
  5525. ErrorCode = ERROR_NOT_ENOUGH_MEMORY;
  5526. goto cleanup;
  5527. }
  5528. ValueBufferSize = Size;
  5529. NumTries++;
  5530. } while (NumTries < 10);
  5531. //
  5532. // If we did not get a valid value from the registry, make up a default
  5533. // one: empty string.
  5534. //
  5535. if (InvalidValue) {
  5536. DefaultValueSize = sizeof(WCHAR);
  5537. if (ValueBufferSize < DefaultValueSize) {
  5538. if (ValueBuffer) {
  5539. PFSVC_ASSERT(ValueBufferSize);
  5540. PFSVC_FREE(ValueBuffer);
  5541. ValueBufferSize = 0;
  5542. }
  5543. ValueBuffer = PFSVC_ALLOC(DefaultValueSize);
  5544. if (!ValueBuffer) {
  5545. ErrorCode = ERROR_NOT_ENOUGH_MEMORY;
  5546. goto cleanup;
  5547. }
  5548. ValueBufferSize = DefaultValueSize;
  5549. }
  5550. ValueBuffer[0] = 0;
  5551. *ReturnSize = DefaultValueSize;
  5552. }
  5553. //
  5554. // We should get here only if we got a value in value buffer.
  5555. //
  5556. PFSVC_ASSERT(ValueBuffer && ValueBufferSize);
  5557. *BuildDefragStatus = ValueBuffer;
  5558. ErrorCode = ERROR_SUCCESS;
  5559. cleanup:
  5560. DBGPR((PFID,PFTRC,"PFSVC: GetBuildDefragStatus(%.80ws)=%x\n",*BuildDefragStatus,ErrorCode));
  5561. if (ErrorCode != ERROR_SUCCESS) {
  5562. if (ValueBuffer) {
  5563. PFSVC_ASSERT(ValueBufferSize);
  5564. PFSVC_FREE(ValueBuffer);
  5565. }
  5566. }
  5567. if (ValueName) {
  5568. PFSVC_FREE(ValueName);
  5569. }
  5570. return ErrorCode;
  5571. }
  5572. DWORD
  5573. PfSvDefragDisks(
  5574. PPFSVC_IDLE_TASK Task
  5575. )
  5576. /*++
  5577. Routine Description:
  5578. If we have not defragged all disks after a setup/upgrade, do so.
  5579. Arguments:
  5580. Task - If specified the function will check Task every once in a
  5581. while to see if it should exit with ERROR_RETRY.
  5582. Return Value:
  5583. Win32 error code.
  5584. --*/
  5585. {
  5586. PNTPATH_TRANSLATION_LIST VolumeList;
  5587. PNTPATH_TRANSLATION_ENTRY VolumeEntry;
  5588. PWCHAR DefraggedVolumeName;
  5589. PWCHAR BuildDefragStatus;
  5590. PWCHAR NewBuildDefragStatus;
  5591. PLIST_ENTRY NextEntry;
  5592. ULONG NewBuildDefragStatusLength;
  5593. ULONG BuildDefragStatusSize;
  5594. ULONG NewBuildDefragStatusSize;
  5595. DWORD ErrorCode;
  5596. BOOLEAN AlreadyDefragged;
  5597. //
  5598. // Initialize locals.
  5599. //
  5600. NewBuildDefragStatus = NULL;
  5601. NewBuildDefragStatusSize = 0;
  5602. BuildDefragStatus = NULL;
  5603. BuildDefragStatusSize = 0;
  5604. VolumeList = NULL;
  5605. DBGPR((PFID,PFTRC,"PFSVC: DefragDisks(%p)\n",Task));
  5606. //
  5607. // Determine defrag status for the current build.
  5608. //
  5609. ErrorCode = PfSvGetBuildDefragStatus(&PfSvcGlobals.OsVersion,
  5610. &BuildDefragStatus,
  5611. &BuildDefragStatusSize);
  5612. if (ErrorCode != ERROR_SUCCESS) {
  5613. goto cleanup;
  5614. }
  5615. //
  5616. // Check if we are already done for this build.
  5617. //
  5618. if (!_wcsicmp(BuildDefragStatus, PFSVC_DEFRAG_DRIVES_DONE)) {
  5619. ErrorCode = ERROR_SUCCESS;
  5620. goto cleanup;
  5621. }
  5622. //
  5623. // Build a list of volumes that are mounted.
  5624. //
  5625. ErrorCode = PfSvBuildNtPathTranslationList(&VolumeList);
  5626. if (ErrorCode != ERROR_SUCCESS) {
  5627. goto cleanup;
  5628. }
  5629. //
  5630. // Walk through the volumes defragging the ones we have not yet
  5631. // defragged after setup.
  5632. //
  5633. for (NextEntry = VolumeList->Flink;
  5634. NextEntry != VolumeList;
  5635. NextEntry = NextEntry->Flink) {
  5636. VolumeEntry = CONTAINING_RECORD(NextEntry,
  5637. NTPATH_TRANSLATION_ENTRY,
  5638. Link);
  5639. //
  5640. // Skip volumes that are not fixed disks.
  5641. //
  5642. if (DRIVE_FIXED != GetDriveType(VolumeEntry->VolumeName)) {
  5643. continue;
  5644. }
  5645. //
  5646. // Have we already defragged this volume?
  5647. //
  5648. AlreadyDefragged = FALSE;
  5649. for (DefraggedVolumeName = BuildDefragStatus;
  5650. DefraggedVolumeName[0] != 0;
  5651. DefraggedVolumeName += wcslen(DefraggedVolumeName) + 1) {
  5652. PFSVC_ASSERT((PCHAR) DefraggedVolumeName < (PCHAR) BuildDefragStatus + BuildDefragStatusSize);
  5653. if (!_wcsicmp(DefraggedVolumeName, VolumeEntry->DosPrefix)) {
  5654. AlreadyDefragged = TRUE;
  5655. break;
  5656. }
  5657. }
  5658. if (AlreadyDefragged) {
  5659. continue;
  5660. }
  5661. //
  5662. // Launch the defragger to defrag this volume.
  5663. //
  5664. ErrorCode = PfSvLaunchDefragger(Task, FALSE, VolumeEntry->DosPrefix);
  5665. if (ErrorCode != ERROR_SUCCESS) {
  5666. goto cleanup;
  5667. }
  5668. //
  5669. // Note that we have defragged this volume.
  5670. //
  5671. NewBuildDefragStatusSize = BuildDefragStatusSize;
  5672. NewBuildDefragStatusSize += (VolumeEntry->DosPrefixLength + 1) * sizeof(WCHAR);
  5673. NewBuildDefragStatus = PFSVC_ALLOC(NewBuildDefragStatusSize);
  5674. if (!NewBuildDefragStatus) {
  5675. ErrorCode = ERROR_NOT_ENOUGH_MEMORY;
  5676. goto cleanup;
  5677. }
  5678. //
  5679. // Start with new defragged drive path.
  5680. //
  5681. wcscpy(NewBuildDefragStatus, VolumeEntry->DosPrefix);
  5682. //
  5683. // Append original status.
  5684. //
  5685. RtlCopyMemory(NewBuildDefragStatus + VolumeEntry->DosPrefixLength + 1,
  5686. BuildDefragStatus,
  5687. BuildDefragStatusSize);
  5688. //
  5689. // The last character and the one before that should be NUL.
  5690. //
  5691. PFSVC_ASSERT(NewBuildDefragStatus[NewBuildDefragStatusSize/sizeof(WCHAR)-1] == 0);
  5692. PFSVC_ASSERT(NewBuildDefragStatus[NewBuildDefragStatusSize/sizeof(WCHAR)-2] == 0);
  5693. //
  5694. // Save the new status.
  5695. //
  5696. ErrorCode = PfSvSetBuildDefragStatus(&PfSvcGlobals.OsVersion,
  5697. NewBuildDefragStatus,
  5698. NewBuildDefragStatusSize);
  5699. if (ErrorCode != ERROR_SUCCESS) {
  5700. goto cleanup;
  5701. }
  5702. //
  5703. // Update the old variable.
  5704. //
  5705. PFSVC_ASSERT(BuildDefragStatus && BuildDefragStatusSize);
  5706. PFSVC_FREE(BuildDefragStatus);
  5707. BuildDefragStatus = NewBuildDefragStatus;
  5708. NewBuildDefragStatus = NULL;
  5709. BuildDefragStatusSize = NewBuildDefragStatusSize;
  5710. NewBuildDefragStatusSize = 0;
  5711. //
  5712. // Continue to check & defrag other volumes.
  5713. //
  5714. }
  5715. //
  5716. // If we came here, then we have successfully defragged all the drives
  5717. // we had to. Set the status in the registry. Note that defrag status
  5718. // value has to end with an additional NUL because it is REG_MULTI_SZ.
  5719. //
  5720. NewBuildDefragStatusSize = (wcslen(PFSVC_DEFRAG_DRIVES_DONE) + 1) * sizeof(WCHAR);
  5721. NewBuildDefragStatusSize += sizeof(WCHAR);
  5722. NewBuildDefragStatus = PFSVC_ALLOC(NewBuildDefragStatusSize);
  5723. if (!NewBuildDefragStatus) {
  5724. ErrorCode = ERROR_NOT_ENOUGH_MEMORY;
  5725. goto cleanup;
  5726. }
  5727. wcscpy(NewBuildDefragStatus, PFSVC_DEFRAG_DRIVES_DONE);
  5728. NewBuildDefragStatus[(NewBuildDefragStatusSize / sizeof(WCHAR)) - 1] = 0;
  5729. ErrorCode = PfSvSetBuildDefragStatus(&PfSvcGlobals.OsVersion,
  5730. NewBuildDefragStatus,
  5731. NewBuildDefragStatusSize);
  5732. //
  5733. // Fall through with error code.
  5734. //
  5735. cleanup:
  5736. DBGPR((PFID,PFTRC,"PFSVC: DefragDisks(%p)=%x\n",Task,ErrorCode));
  5737. if (BuildDefragStatus) {
  5738. //
  5739. // We should have NULL'ed NewBuildDefragStatus, otherwise we will
  5740. // try to free the same memory twice.
  5741. //
  5742. PFSVC_ASSERT(BuildDefragStatus != NewBuildDefragStatus);
  5743. PFSVC_ASSERT(BuildDefragStatusSize);
  5744. PFSVC_FREE(BuildDefragStatus);
  5745. }
  5746. if (NewBuildDefragStatus) {
  5747. PFSVC_ASSERT(NewBuildDefragStatusSize);
  5748. PFSVC_FREE(NewBuildDefragStatus);
  5749. }
  5750. if (VolumeList) {
  5751. PfSvFreeNtPathTranslationList(VolumeList);
  5752. }
  5753. return ErrorCode;
  5754. }
  5755. //
  5756. // Routines to cleanup old scenario files in the prefetch directory.
  5757. //
  5758. DWORD
  5759. PfSvCleanupPrefetchDirectory(
  5760. PPFSVC_IDLE_TASK Task
  5761. )
  5762. /*++
  5763. Routine Description:
  5764. If we have too many scenario files in the prefetch directory, discard the
  5765. ones that are not as useful to make room for new files.
  5766. Arguments:
  5767. Task - If specified the function will check Task every once in a
  5768. while to see if it should exit with ERROR_RETRY.
  5769. Return Value:
  5770. Win32 error code.
  5771. --*/
  5772. {
  5773. PPFSVC_SCENARIO_AGE_INFO Scenarios;
  5774. PFSVC_SCENARIO_FILE_CURSOR FileCursor;
  5775. PPF_SCENARIO_HEADER Scenario;
  5776. ULONG NumPrefetchFiles;
  5777. ULONG AllocationSize;
  5778. ULONG NumScenarios;
  5779. ULONG ScenarioIdx;
  5780. ULONG PrefetchFileIdx;
  5781. ULONG FileSize;
  5782. ULONG FailedCheck;
  5783. ULONG MaxRemainingScenarioFiles;
  5784. ULONG NumLaunches;
  5785. ULONG HoursSinceLastLaunch;
  5786. FILETIME CurrentTime;
  5787. ULARGE_INTEGER CurrentTimeLI;
  5788. ULARGE_INTEGER LastLaunchTimeLI;
  5789. DWORD ErrorCode;
  5790. BOOLEAN AcquiredLock;
  5791. //
  5792. // Initialize locals.
  5793. //
  5794. AcquiredLock = FALSE;
  5795. NumScenarios = 0;
  5796. Scenarios = NULL;
  5797. Scenario = NULL;
  5798. GetSystemTimeAsFileTime(&CurrentTime);
  5799. PfSvInitializeScenarioFileCursor(&FileCursor);
  5800. CurrentTimeLI.LowPart = CurrentTime.dwLowDateTime;
  5801. CurrentTimeLI.HighPart = CurrentTime.dwHighDateTime;
  5802. DBGPR((PFID,PFTRC,"PFSVC: CleanupPrefetchDirectory(%p)\n",Task));
  5803. //
  5804. // Once we are done cleaning up, we should not have more than this many
  5805. // prefetch files remaining.
  5806. //
  5807. MaxRemainingScenarioFiles = PFSVC_MAX_PREFETCH_FILES / 4;
  5808. PFSVC_ACQUIRE_LOCK(PfSvcGlobals.PrefetchRootLock);
  5809. AcquiredLock = TRUE;
  5810. //
  5811. // Start the file cursor.
  5812. //
  5813. ErrorCode = PfSvStartScenarioFileCursor(&FileCursor, PfSvcGlobals.PrefetchRoot);
  5814. if (ErrorCode != ERROR_SUCCESS) {
  5815. goto cleanup;
  5816. }
  5817. //
  5818. // Count the number of files in the directory.
  5819. //
  5820. ErrorCode = PfSvCountFilesInDirectory(PfSvcGlobals.PrefetchRoot,
  5821. L"*." PF_PREFETCH_FILE_EXTENSION,
  5822. &NumPrefetchFiles);
  5823. if (ErrorCode != ERROR_SUCCESS) {
  5824. goto cleanup;
  5825. }
  5826. PFSVC_RELEASE_LOCK(PfSvcGlobals.PrefetchRootLock);
  5827. AcquiredLock = FALSE;
  5828. //
  5829. // Allocate an array that we will fill in with information from
  5830. // scenario files to determine which ones need to be discarded.
  5831. //
  5832. AllocationSize = NumPrefetchFiles * sizeof(PFSVC_SCENARIO_AGE_INFO);
  5833. Scenarios = PFSVC_ALLOC(AllocationSize);
  5834. if (!Scenarios) {
  5835. ErrorCode = ERROR_NOT_ENOUGH_MEMORY;
  5836. goto cleanup;
  5837. }
  5838. //
  5839. // Initialize the scenarios array so we know what to clean up.
  5840. //
  5841. RtlZeroMemory(Scenarios, AllocationSize);
  5842. NumScenarios = 0;
  5843. //
  5844. // Enumerate the scenario files:
  5845. //
  5846. ScenarioIdx = 0;
  5847. PrefetchFileIdx = 0;
  5848. do {
  5849. //
  5850. // Should we continue to run?
  5851. //
  5852. ErrorCode = PfSvContinueRunningTask(Task);
  5853. if (ErrorCode != ERROR_SUCCESS) {
  5854. goto cleanup;
  5855. }
  5856. //
  5857. // Get the file info for the next scenario file.
  5858. //
  5859. ErrorCode = PfSvGetNextScenarioFileInfo(&FileCursor);
  5860. if (ErrorCode == ERROR_NO_MORE_FILES) {
  5861. break;
  5862. }
  5863. if (ErrorCode != ERROR_SUCCESS) {
  5864. goto cleanup;
  5865. }
  5866. //
  5867. // Is the file name longer than what a valid prefetch file can be max?
  5868. //
  5869. if (FileCursor.FileNameLength > PF_MAX_SCENARIO_FILE_NAME) {
  5870. //
  5871. // Bogus file. Remove it.
  5872. //
  5873. DeleteFile(FileCursor.FilePath);
  5874. goto NextPrefetchFile;
  5875. }
  5876. //
  5877. // Map the file.
  5878. //
  5879. ErrorCode = PfSvGetViewOfFile(FileCursor.FilePath,
  5880. &Scenario,
  5881. &FileSize);
  5882. if (ErrorCode != ERROR_SUCCESS) {
  5883. goto NextPrefetchFile;
  5884. }
  5885. //
  5886. // Verify the scenario file.
  5887. //
  5888. if (!PfSvVerifyScenarioBuffer(Scenario, FileSize, &FailedCheck) ||
  5889. Scenario->ServiceVersion != PFSVC_SERVICE_VERSION) {
  5890. DeleteFile(FileCursor.FilePath);
  5891. goto NextPrefetchFile;
  5892. }
  5893. //
  5894. // Skip boot scenario, we won't discard it.
  5895. //
  5896. if (Scenario->ScenarioType == PfSystemBootScenarioType) {
  5897. goto NextPrefetchFile;
  5898. }
  5899. //
  5900. // Determine the last time scenario was updated. I assume this
  5901. // corresponds to the last time scenario was launched...
  5902. //
  5903. LastLaunchTimeLI.LowPart = FileCursor.FileData.ftLastWriteTime.dwLowDateTime;
  5904. LastLaunchTimeLI.HighPart = FileCursor.FileData.ftLastWriteTime.dwHighDateTime;
  5905. if (CurrentTimeLI.QuadPart > LastLaunchTimeLI.QuadPart) {
  5906. HoursSinceLastLaunch = (ULONG) ((CurrentTimeLI.QuadPart - LastLaunchTimeLI.QuadPart) /
  5907. PFSVC_NUM_100NS_IN_AN_HOUR);
  5908. } else {
  5909. HoursSinceLastLaunch = 0;
  5910. }
  5911. //
  5912. // Calculate weight: bigger weight means scenario file won't get
  5913. // discarded. We calculate the weight by dividing the total number
  5914. // times a scenario was launched by how long has it been since the last
  5915. // launch of the scenario.
  5916. //
  5917. NumLaunches = Scenario->NumLaunches;
  5918. //
  5919. // For the calculations below limit how large NumLaunches can be, so
  5920. // values does not overflow.
  5921. //
  5922. if (NumLaunches > 1 * 1024 * 1024) {
  5923. NumLaunches = 1 * 1024 * 1024;
  5924. }
  5925. //
  5926. // Since we are going divide by number of hours (e.g. 7*24 for a program
  5927. // launched a week ago) multiplying the number of launches with a number
  5928. // allows us to give a weight other than 0 to scenarios launched long ago.
  5929. //
  5930. Scenarios[ScenarioIdx].Weight = NumLaunches * 256;
  5931. if (HoursSinceLastLaunch) {
  5932. Scenarios[ScenarioIdx].Weight /= HoursSinceLastLaunch;
  5933. }
  5934. //
  5935. // Copy over the file path.
  5936. //
  5937. AllocationSize = (FileCursor.FilePathLength + 1) * sizeof(WCHAR);
  5938. Scenarios[ScenarioIdx].FilePath = PFSVC_ALLOC(AllocationSize);
  5939. if (!Scenarios[ScenarioIdx].FilePath) {
  5940. ErrorCode = ERROR_NOT_ENOUGH_MEMORY;
  5941. goto cleanup;
  5942. }
  5943. wcscpy(Scenarios[ScenarioIdx].FilePath, FileCursor.FilePath);
  5944. ScenarioIdx++;
  5945. NextPrefetchFile:
  5946. PrefetchFileIdx++;
  5947. if (Scenario) {
  5948. UnmapViewOfFile(Scenario);
  5949. Scenario = NULL;
  5950. }
  5951. } while (PrefetchFileIdx < NumPrefetchFiles);
  5952. //
  5953. // If we do not have too many scenario files, we don't have to do anything.
  5954. //
  5955. NumScenarios = ScenarioIdx;
  5956. if (NumScenarios <= MaxRemainingScenarioFiles) {
  5957. ErrorCode = ERROR_SUCCESS;
  5958. goto cleanup;
  5959. }
  5960. //
  5961. // Sort the age information.
  5962. //
  5963. qsort(Scenarios,
  5964. NumScenarios,
  5965. sizeof(PFSVC_SCENARIO_AGE_INFO),
  5966. PfSvCompareScenarioAgeInfo);
  5967. //
  5968. // Delete the files with the smallest weight until we reach our goal.
  5969. //
  5970. for (ScenarioIdx = 0;
  5971. (ScenarioIdx < NumScenarios) &&
  5972. ((NumScenarios - ScenarioIdx) > MaxRemainingScenarioFiles);
  5973. ScenarioIdx++) {
  5974. //
  5975. // Should we continue to run?
  5976. //
  5977. ErrorCode = PfSvContinueRunningTask(Task);
  5978. if (ErrorCode != ERROR_SUCCESS) {
  5979. goto cleanup;
  5980. }
  5981. DeleteFile(Scenarios[ScenarioIdx].FilePath);
  5982. }
  5983. //
  5984. // Count the files in the directory now and update the global.
  5985. //
  5986. ErrorCode = PfSvCountFilesInDirectory(PfSvcGlobals.PrefetchRoot,
  5987. L"*." PF_PREFETCH_FILE_EXTENSION,
  5988. &NumPrefetchFiles);
  5989. if (ErrorCode != ERROR_SUCCESS) {
  5990. goto cleanup;
  5991. }
  5992. //
  5993. // Note that global NumPrefetchFiles is not protected, so the new value
  5994. // we are setting it to may be overwritten with an older value. It should
  5995. // not be a big problem though, maybe resulting in this task being requeued.
  5996. //
  5997. PfSvcGlobals.NumPrefetchFiles = NumPrefetchFiles;
  5998. ErrorCode = ERROR_SUCCESS;
  5999. cleanup:
  6000. DBGPR((PFID,PFTRC,"PFSVC: CleanupPrefetchDirectory(%p)=%x\n",Task,ErrorCode));
  6001. if (AcquiredLock) {
  6002. PFSVC_RELEASE_LOCK(PfSvcGlobals.PrefetchRootLock);
  6003. }
  6004. PfSvCleanupScenarioFileCursor(&FileCursor);
  6005. if (Scenarios) {
  6006. for (ScenarioIdx = 0; ScenarioIdx < NumScenarios; ScenarioIdx++) {
  6007. if (Scenarios[ScenarioIdx].FilePath) {
  6008. PFSVC_FREE(Scenarios[ScenarioIdx].FilePath);
  6009. }
  6010. }
  6011. PFSVC_FREE(Scenarios);
  6012. }
  6013. if (Scenario) {
  6014. UnmapViewOfFile(Scenario);
  6015. }
  6016. return ErrorCode;
  6017. }
  6018. int
  6019. __cdecl
  6020. PfSvCompareScenarioAgeInfo(
  6021. const void *Param1,
  6022. const void *Param2
  6023. )
  6024. /*++
  6025. Routine Description:
  6026. Qsort comparison function for PFSVC_SCENARIO_AGE_INFO structure.
  6027. Arguments:
  6028. Param1, Param2 - pointer to PFSVC_SCENARIO_AGE_INFO structures
  6029. Return Value:
  6030. Qsort comparison function return value.
  6031. --*/
  6032. {
  6033. PFSVC_SCENARIO_AGE_INFO *Elem1;
  6034. PFSVC_SCENARIO_AGE_INFO *Elem2;
  6035. Elem1 = (PVOID) Param1;
  6036. Elem2 = (PVOID) Param2;
  6037. //
  6038. // Compare precalculated weights.
  6039. //
  6040. if (Elem1->Weight > Elem2->Weight) {
  6041. return 1;
  6042. } else if (Elem1->Weight < Elem2->Weight) {
  6043. return -1;
  6044. } else {
  6045. return 0;
  6046. }
  6047. }
  6048. //
  6049. // Routines to enumerate scenario files.
  6050. //
  6051. VOID
  6052. PfSvInitializeScenarioFileCursor (
  6053. PPFSVC_SCENARIO_FILE_CURSOR FileCursor
  6054. )
  6055. /*++
  6056. Routine Description:
  6057. Initializes the cursor structure so it can be safely cleaned up.
  6058. Arguments:
  6059. FileCursor - Pointer to structure.
  6060. Return Value:
  6061. None.
  6062. --*/
  6063. {
  6064. FileCursor->FilePath = NULL;
  6065. FileCursor->FileNameLength = 0;
  6066. FileCursor->FilePathLength = 0;
  6067. FileCursor->CurrentFileIdx = 0;
  6068. FileCursor->PrefetchRoot = NULL;
  6069. FileCursor->FindFileHandle = INVALID_HANDLE_VALUE;
  6070. return;
  6071. }
  6072. VOID
  6073. PfSvCleanupScenarioFileCursor(
  6074. PPFSVC_SCENARIO_FILE_CURSOR FileCursor
  6075. )
  6076. /*++
  6077. Routine Description:
  6078. Cleans up an initialized and possibly started cursor structure.
  6079. Arguments:
  6080. FileCursor - Pointer to structure.
  6081. Return Value:
  6082. None.
  6083. --*/
  6084. {
  6085. if (FileCursor->FilePath) {
  6086. PFSVC_FREE(FileCursor->FilePath);
  6087. }
  6088. if (FileCursor->PrefetchRoot) {
  6089. PFSVC_FREE(FileCursor->PrefetchRoot);
  6090. }
  6091. if (FileCursor->FindFileHandle != INVALID_HANDLE_VALUE) {
  6092. FindClose(FileCursor->FindFileHandle);
  6093. }
  6094. return;
  6095. }
  6096. DWORD
  6097. PfSvStartScenarioFileCursor(
  6098. PPFSVC_SCENARIO_FILE_CURSOR FileCursor,
  6099. WCHAR *PrefetchRoot
  6100. )
  6101. /*++
  6102. Routine Description:
  6103. After making this call on an initialized FileCursor, you can start
  6104. enumerating the scenario files in that directory by calling the get
  6105. next file function.
  6106. You have to call the get next file function after starting the cursor
  6107. to get the information on the first file.
  6108. If this function fails, you should call cleanup on the FileCursor
  6109. structure and reinitialize it before trying to start the cursor again.
  6110. Arguments:
  6111. FileCursor - Pointer to initialized cursor structure.
  6112. PrefetchRoot - Directory path in which we'll look for prefetch
  6113. files.
  6114. Return Value:
  6115. Win32 error code.
  6116. --*/
  6117. {
  6118. WCHAR *PrefetchFileSearchPattern;
  6119. WCHAR *PrefetchFileSearchPath;
  6120. ULONG PrefetchRootLength;
  6121. ULONG PrefetchFileSearchPathLength;
  6122. ULONG FileNameMaxLength;
  6123. ULONG FilePathMaxLength;
  6124. DWORD ErrorCode;
  6125. //
  6126. // Initialize locals.
  6127. //
  6128. PrefetchRootLength = wcslen(PrefetchRoot);
  6129. PrefetchFileSearchPattern = L"\\*." PF_PREFETCH_FILE_EXTENSION;
  6130. PrefetchFileSearchPath = NULL;
  6131. //
  6132. // The file cursor should have been initialized.
  6133. //
  6134. PFSVC_ASSERT(!FileCursor->CurrentFileIdx);
  6135. PFSVC_ASSERT(!FileCursor->PrefetchRoot);
  6136. PFSVC_ASSERT(FileCursor->FindFileHandle == INVALID_HANDLE_VALUE);
  6137. //
  6138. // Copy the prefetch root directory path.
  6139. //
  6140. FileCursor->PrefetchRoot = PFSVC_ALLOC((PrefetchRootLength + 1) * sizeof(WCHAR));
  6141. if (!FileCursor->PrefetchRoot) {
  6142. ErrorCode = ERROR_NOT_ENOUGH_MEMORY;
  6143. goto cleanup;
  6144. }
  6145. wcscpy(FileCursor->PrefetchRoot, PrefetchRoot);
  6146. FileCursor->PrefetchRootLength = PrefetchRootLength;
  6147. //
  6148. // Build the path we will pass in to enumerate the prefetch files.
  6149. //
  6150. PrefetchFileSearchPathLength = PrefetchRootLength;
  6151. PrefetchFileSearchPathLength += wcslen(PrefetchFileSearchPattern);
  6152. PrefetchFileSearchPath = PFSVC_ALLOC((PrefetchFileSearchPathLength + 1) * sizeof(WCHAR));
  6153. if (!PrefetchFileSearchPath) {
  6154. ErrorCode = ERROR_NOT_ENOUGH_MEMORY;
  6155. goto cleanup;
  6156. }
  6157. wcscpy(PrefetchFileSearchPath, PrefetchRoot);
  6158. wcscat(PrefetchFileSearchPath, PrefetchFileSearchPattern);
  6159. //
  6160. // Allocate the string we will use to build the full path of the
  6161. // prefetch files. We can use it for prefetch files with names of
  6162. // max MAX_PATH. This works because that is the max file name that
  6163. // can fit into WIN32_FIND_DATA structure.
  6164. //
  6165. FileNameMaxLength = MAX_PATH;
  6166. FilePathMaxLength = PrefetchRootLength;
  6167. FilePathMaxLength += wcslen(L"\\");
  6168. FilePathMaxLength += FileNameMaxLength;
  6169. FileCursor->FilePath = PFSVC_ALLOC((FilePathMaxLength + 1) * sizeof(WCHAR));
  6170. if (!FileCursor->FilePath) {
  6171. ErrorCode = ERROR_NOT_ENOUGH_MEMORY;
  6172. goto cleanup;
  6173. }
  6174. //
  6175. // Initialize the first part of the file path and note where we will
  6176. // start copying file names from.
  6177. //
  6178. wcscpy(FileCursor->FilePath, PrefetchRoot);
  6179. wcscat(FileCursor->FilePath, L"\\");
  6180. FileCursor->FileNameStart = PrefetchRootLength + 1;
  6181. //
  6182. // Start enumerating the files. Note that this puts the data for the
  6183. // first file into FileData member.
  6184. //
  6185. FileCursor->FindFileHandle = FindFirstFile(PrefetchFileSearchPath,
  6186. &FileCursor->FileData);
  6187. if (FileCursor->FindFileHandle == INVALID_HANDLE_VALUE) {
  6188. ErrorCode = GetLastError();
  6189. goto cleanup;
  6190. }
  6191. ErrorCode = ERROR_SUCCESS;
  6192. cleanup:
  6193. DBGPR((PFID,PFTRC,"PFSVC: StartFileCursor(%p,%ws)=%x\n",FileCursor,PrefetchRoot,ErrorCode));
  6194. if (PrefetchFileSearchPath) {
  6195. PFSVC_FREE(PrefetchFileSearchPath);
  6196. }
  6197. return ErrorCode;
  6198. }
  6199. DWORD
  6200. PfSvGetNextScenarioFileInfo(
  6201. PPFSVC_SCENARIO_FILE_CURSOR FileCursor
  6202. )
  6203. /*++
  6204. Routine Description:
  6205. Fills in public fields of the FileCursor with information on the next
  6206. scenario file.
  6207. You have to call the get next file function after starting the cursor
  6208. to get the information on the first file.
  6209. Files with *names* longer than MAX_PATH will be skipped because it
  6210. is not feasible to handle these with Win32 API.
  6211. Arguments:
  6212. FileCursor - Pointer to started cursor structure.
  6213. Return Value:
  6214. ERROR_NO_MORE_FILES - No more files to enumerate.
  6215. Win32 error code.
  6216. --*/
  6217. {
  6218. DWORD ErrorCode;
  6219. //
  6220. // File cursor should have been started.
  6221. //
  6222. PFSVC_ASSERT(FileCursor->PrefetchRoot);
  6223. PFSVC_ASSERT(FileCursor->FindFileHandle != INVALID_HANDLE_VALUE);
  6224. //
  6225. // If this it the first file, the FileData for it was already set when
  6226. // we started the cursor. Otherwise call FindNextFile.
  6227. //
  6228. if (FileCursor->CurrentFileIdx != 0) {
  6229. if (!FindNextFile(FileCursor->FindFileHandle, &FileCursor->FileData)) {
  6230. ErrorCode = GetLastError();
  6231. goto cleanup;
  6232. }
  6233. }
  6234. FileCursor->FileNameLength = wcslen(FileCursor->FileData.cFileName);
  6235. //
  6236. // We allocated a file path to hold MAX_PATH file name in addition to the
  6237. // directory path. FileData.cFileName is MAX_PATH sized.
  6238. //
  6239. PFSVC_ASSERT(FileCursor->FileNameLength < MAX_PATH);
  6240. if (FileCursor->FileNameLength >= MAX_PATH) {
  6241. ErrorCode = ERROR_BAD_FORMAT;
  6242. goto cleanup;
  6243. }
  6244. //
  6245. // Copy the file name.
  6246. //
  6247. wcscpy(FileCursor->FilePath + FileCursor->FileNameStart,
  6248. FileCursor->FileData.cFileName);
  6249. FileCursor->FilePathLength = FileCursor->FileNameStart + FileCursor->FileNameLength;
  6250. FileCursor->CurrentFileIdx++;
  6251. ErrorCode = ERROR_SUCCESS;
  6252. cleanup:
  6253. DBGPR((PFID,PFTRC,"PFSVC: GetNextScenarioFile(%p)=%ws,%x\n",FileCursor,FileCursor->FileData.cFileName,ErrorCode));
  6254. return ErrorCode;
  6255. }
  6256. //
  6257. // File I/O utility routines.
  6258. //
  6259. DWORD
  6260. PfSvGetViewOfFile(
  6261. IN WCHAR *FilePath,
  6262. OUT PVOID *BasePointer,
  6263. OUT PULONG FileSize
  6264. )
  6265. /*++
  6266. Routine Description:
  6267. Map the all of the specified file to memory.
  6268. Arguments:
  6269. FilePath - NUL terminated path to file to map.
  6270. BasePointer - Start address of mapping will be returned here.
  6271. FileSize - Size of the mapping/file will be returned here.
  6272. Return Value:
  6273. Win32 error code.
  6274. --*/
  6275. {
  6276. HANDLE InputHandle;
  6277. HANDLE InputMappingHandle;
  6278. DWORD ErrorCode;
  6279. DWORD SizeL;
  6280. DWORD SizeH;
  6281. BOOLEAN OpenedFile;
  6282. BOOLEAN CreatedFileMapping;
  6283. //
  6284. // Initialize locals.
  6285. //
  6286. OpenedFile = FALSE;
  6287. CreatedFileMapping = FALSE;
  6288. DBGPR((PFID,PFTRC,"PFSVC: GetViewOfFile(%ws)\n", FilePath));
  6289. //
  6290. // Note that we are opening the file exclusively. This guarantees
  6291. // that for trace files as long as the kernel is not done writing
  6292. // it we can't open the file, which guarantees we won't have an
  6293. // incomplete file to worry about.
  6294. //
  6295. InputHandle = CreateFile(FilePath,
  6296. GENERIC_READ,
  6297. 0,
  6298. NULL,
  6299. OPEN_EXISTING,
  6300. 0,
  6301. NULL);
  6302. if (INVALID_HANDLE_VALUE == InputHandle)
  6303. {
  6304. ErrorCode = GetLastError();
  6305. goto cleanup;
  6306. }
  6307. OpenedFile = TRUE;
  6308. SizeL = GetFileSize(InputHandle, &SizeH);
  6309. if (SizeL == INVALID_FILE_SIZE && (GetLastError() != NO_ERROR )) {
  6310. ErrorCode = GetLastError();
  6311. goto cleanup;
  6312. }
  6313. if (SizeH) {
  6314. ErrorCode = ERROR_BAD_LENGTH;
  6315. goto cleanup;
  6316. }
  6317. if (FileSize) {
  6318. *FileSize = SizeL;
  6319. }
  6320. InputMappingHandle = CreateFileMapping(InputHandle,
  6321. 0,
  6322. PAGE_READONLY,
  6323. 0,
  6324. 0,
  6325. NULL);
  6326. if (NULL == InputMappingHandle)
  6327. {
  6328. ErrorCode = GetLastError();
  6329. goto cleanup;
  6330. }
  6331. CreatedFileMapping = TRUE;
  6332. *BasePointer = MapViewOfFile(InputMappingHandle,
  6333. FILE_MAP_READ,
  6334. 0,
  6335. 0,
  6336. 0);
  6337. if (NULL == *BasePointer) {
  6338. ErrorCode = GetLastError();
  6339. goto cleanup;
  6340. }
  6341. ErrorCode = ERROR_SUCCESS;
  6342. cleanup:
  6343. if (OpenedFile) {
  6344. CloseHandle(InputHandle);
  6345. }
  6346. if (CreatedFileMapping) {
  6347. CloseHandle(InputMappingHandle);
  6348. }
  6349. DBGPR((PFID,PFTRC,"PFSVC: GetViewOfFile(%ws)=%x\n", FilePath, ErrorCode));
  6350. return ErrorCode;
  6351. }
  6352. DWORD
  6353. PfSvWriteBuffer(
  6354. PWCHAR FilePath,
  6355. PVOID Buffer,
  6356. ULONG Length
  6357. )
  6358. /*++
  6359. Routine Description:
  6360. This routine creats/overwrites the file at the specified path and
  6361. writes the contents of the buffer to it.
  6362. Arguments:
  6363. FilePath - Full path to the file.
  6364. Buffer - Buffer to write out.
  6365. Length - Number of bytes to write out from the buffer.
  6366. Return Value:
  6367. Win32 error code.
  6368. --*/
  6369. {
  6370. DWORD BytesWritten;
  6371. HANDLE OutputHandle;
  6372. DWORD ErrorCode;
  6373. BOOL Result;
  6374. //
  6375. // Initialize locals.
  6376. //
  6377. OutputHandle = INVALID_HANDLE_VALUE;
  6378. DBGPR((PFID,PFSTRC,"PFSVC: WriteBuffer(%p,%ws)\n", Buffer, FilePath));
  6379. //
  6380. // Open file overwriting any existing one. Don't share it so
  6381. // nobody tries to read a half-written file.
  6382. //
  6383. OutputHandle = CreateFile(FilePath,
  6384. GENERIC_READ | GENERIC_WRITE,
  6385. 0,
  6386. NULL,
  6387. CREATE_ALWAYS,
  6388. 0,
  6389. NULL);
  6390. if (INVALID_HANDLE_VALUE == OutputHandle)
  6391. {
  6392. ErrorCode = GetLastError();
  6393. goto cleanup;
  6394. }
  6395. //
  6396. // Write out the scenario.
  6397. //
  6398. Result = WriteFile(OutputHandle,
  6399. Buffer,
  6400. Length,
  6401. &BytesWritten,
  6402. NULL);
  6403. if (!Result || (BytesWritten != Length)) {
  6404. ErrorCode = GetLastError();
  6405. goto cleanup;
  6406. }
  6407. ErrorCode = ERROR_SUCCESS;
  6408. cleanup:
  6409. if (OutputHandle != INVALID_HANDLE_VALUE) {
  6410. CloseHandle(OutputHandle);
  6411. }
  6412. DBGPR((PFID,PFSTRC,"PFSVC: WriteBuffer(%p,%ws)=%x\n", Buffer, FilePath, ErrorCode));
  6413. return ErrorCode;
  6414. }
  6415. DWORD
  6416. PfSvGetLastWriteTime (
  6417. WCHAR *FilePath,
  6418. PFILETIME LastWriteTime
  6419. )
  6420. /*++
  6421. Routine Description:
  6422. This function attempts to get the last write time for the
  6423. specified file.
  6424. Arguments:
  6425. FilePath - Pointer to NUL terminated path.
  6426. LastWriteTime - Pointer to return buffer.
  6427. Return Value:
  6428. Win32 error code.
  6429. --*/
  6430. {
  6431. HANDLE FileHandle;
  6432. DWORD ErrorCode;
  6433. //
  6434. // Initialize locals.
  6435. //
  6436. FileHandle = INVALID_HANDLE_VALUE;
  6437. //
  6438. // Open the file.
  6439. //
  6440. FileHandle = CreateFile(FilePath,
  6441. GENERIC_READ,
  6442. FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
  6443. NULL,
  6444. OPEN_EXISTING,
  6445. FILE_FLAG_BACKUP_SEMANTICS,
  6446. NULL);
  6447. if (FileHandle == INVALID_HANDLE_VALUE) {
  6448. ErrorCode = GetLastError();
  6449. goto cleanup;
  6450. }
  6451. //
  6452. // Query last write time.
  6453. //
  6454. if (!GetFileTime(FileHandle, NULL, NULL, LastWriteTime)) {
  6455. ErrorCode = GetLastError();
  6456. goto cleanup;
  6457. }
  6458. ErrorCode = ERROR_SUCCESS;
  6459. cleanup:
  6460. if (FileHandle != INVALID_HANDLE_VALUE) {
  6461. CloseHandle(FileHandle);
  6462. }
  6463. return ErrorCode;
  6464. }
  6465. DWORD
  6466. PfSvReadLine (
  6467. FILE *File,
  6468. WCHAR **LineBuffer,
  6469. ULONG *LineBufferMaxChars,
  6470. ULONG *LineLength
  6471. )
  6472. /*++
  6473. Routine Description:
  6474. This function reads a line from the specified file into
  6475. LineBuffer. If LineBuffer is NULL or not big enough, it is
  6476. allocated or reallocated using PFSVC_ALLOC/FREE macros. It is the
  6477. caller's reponsibility to free the returned buffer.
  6478. Carriage return/Line feed characters are included in the returned
  6479. LineBuffer & LineLength. Thus, a LineLength of 0 means end of file
  6480. is hit. Returned LineBuffer is NUL terminated.
  6481. Arguments:
  6482. File - File to read from.
  6483. LineBuffer - Pointer to a buffer to read the line into.
  6484. LineBufferMaxChars - Pointer to size of LineBuffer in characters,
  6485. including room for NUL etc.
  6486. LineLength - Pointer to length of the read line in characters
  6487. including the carriage return/linefeed, excluding NUL.
  6488. Return Value:
  6489. Win32 error code.
  6490. --*/
  6491. {
  6492. DWORD ErrorCode;
  6493. WCHAR *NewBuffer;
  6494. ULONG NewBufferMaxChars;
  6495. WCHAR *CurrentReadPosition;
  6496. ULONG MaxCharsToRead;
  6497. //
  6498. // Verify parameters.
  6499. //
  6500. PFSVC_ASSERT(LineBuffer && LineBufferMaxChars && LineLength);
  6501. if (*LineBufferMaxChars) {
  6502. PFSVC_ASSERT(*LineBuffer);
  6503. }
  6504. //
  6505. // If a zero length but non NULL buffer was passed in, free it so
  6506. // we can allocate a larger initial one.
  6507. //
  6508. if (((*LineBufferMaxChars) == 0) && (*LineBuffer)) {
  6509. PFSVC_FREE(*LineBuffer);
  6510. (*LineBuffer) = NULL;
  6511. }
  6512. //
  6513. // If no buffer was passed in, allocate one. We do not want to
  6514. // enter the read line loop with a zero length or NULL buffer.
  6515. //
  6516. if (!(*LineBuffer)) {
  6517. PFSVC_ASSERT((*LineBufferMaxChars) == 0);
  6518. (*LineBuffer) = PFSVC_ALLOC(MAX_PATH * sizeof(WCHAR));
  6519. if (!(*LineBuffer)) {
  6520. ErrorCode = ERROR_INSUFFICIENT_BUFFER;
  6521. goto cleanup;
  6522. }
  6523. (*LineBufferMaxChars) = MAX_PATH;
  6524. }
  6525. //
  6526. // Initialize output length and NUL terminate the output line.
  6527. //
  6528. (*LineLength) = 0;
  6529. (*(*LineBuffer)) = 0;
  6530. do {
  6531. //
  6532. // Try to read a line from the file.
  6533. //
  6534. CurrentReadPosition = (*LineBuffer) + (*LineLength);
  6535. MaxCharsToRead = (*LineBufferMaxChars) - (*LineLength);
  6536. if (!fgetws(CurrentReadPosition,
  6537. MaxCharsToRead,
  6538. File)) {
  6539. //
  6540. // If we have not hit an EOF, we have hit an error.
  6541. //
  6542. if (!feof(File)) {
  6543. ErrorCode = ERROR_READ_FAULT;
  6544. goto cleanup;
  6545. } else {
  6546. //
  6547. // We hit end of file. Return what we have.
  6548. //
  6549. ErrorCode = ERROR_SUCCESS;
  6550. goto cleanup;
  6551. }
  6552. }
  6553. //
  6554. // Update line length.
  6555. //
  6556. (*LineLength) += wcslen(CurrentReadPosition);
  6557. //
  6558. // If we have read a carriage return, we are done. Check to
  6559. // see if we had room to read anything first!
  6560. //
  6561. if ((*LineLength) && (*LineBuffer)[(*LineLength) - 1] == L'\n') {
  6562. break;
  6563. }
  6564. //
  6565. // If we read up to the end of the buffer, resize it.
  6566. //
  6567. if ((*LineLength) == (*LineBufferMaxChars) - 1) {
  6568. //
  6569. // We should not enter this loop with a zero lengthed or NULL
  6570. // line buffer.
  6571. //
  6572. PFSVC_ASSERT((*LineBufferMaxChars) && (*LineBuffer));
  6573. NewBufferMaxChars = (*LineBufferMaxChars) * 2;
  6574. NewBuffer = PFSVC_ALLOC(NewBufferMaxChars * sizeof(WCHAR));
  6575. if (!NewBuffer) {
  6576. ErrorCode = ERROR_NOT_ENOUGH_MEMORY;
  6577. goto cleanup;
  6578. }
  6579. //
  6580. // Copy contents of the original buffer and free it.
  6581. //
  6582. RtlCopyMemory(NewBuffer,
  6583. (*LineBuffer),
  6584. ((*LineLength) + 1) * sizeof(WCHAR));
  6585. PFSVC_FREE(*LineBuffer);
  6586. //
  6587. // Update line buffer.
  6588. //
  6589. (*LineBuffer) = NewBuffer;
  6590. (*LineBufferMaxChars) = NewBufferMaxChars;
  6591. }
  6592. //
  6593. // Continue reading this line and appending it to output
  6594. // buffer.
  6595. //
  6596. } while (TRUE);
  6597. ErrorCode = ERROR_SUCCESS;
  6598. cleanup:
  6599. if (ErrorCode == ERROR_SUCCESS && (*LineBufferMaxChars)) {
  6600. //
  6601. // Returned length must fit into buffer.
  6602. //
  6603. PFSVC_ASSERT((*LineLength) < (*LineBufferMaxChars));
  6604. //
  6605. // Returned buffer should be NUL terminated.
  6606. //
  6607. PFSVC_ASSERT((*LineBuffer)[(*LineLength)] == 0);
  6608. }
  6609. return ErrorCode;
  6610. }
  6611. DWORD
  6612. PfSvGetFileBasicInformation (
  6613. WCHAR *FilePath,
  6614. PFILE_BASIC_INFORMATION FileInformation
  6615. )
  6616. /*++
  6617. Routine Description:
  6618. This routine queries the basic attributes for the specified file.
  6619. Arguments:
  6620. FilePath - Pointer to full NT file path, e.g.
  6621. \Device\HarddiskVolume1\boot.ini, NOT Win32 path, e.g. c:\boot.ini
  6622. FileInformation - If successful the basic file info is returned here.
  6623. Return Value:
  6624. Win32 error code.
  6625. --*/
  6626. {
  6627. OBJECT_ATTRIBUTES ObjectAttributes;
  6628. UNICODE_STRING FilePathU;
  6629. NTSTATUS Status;
  6630. DWORD ErrorCode;
  6631. //
  6632. // Query the file information.
  6633. //
  6634. RtlInitUnicodeString(&FilePathU, FilePath);
  6635. InitializeObjectAttributes(&ObjectAttributes,
  6636. &FilePathU,
  6637. OBJ_CASE_INSENSITIVE,
  6638. NULL,
  6639. NULL);
  6640. Status = NtQueryAttributesFile(&ObjectAttributes,
  6641. FileInformation);
  6642. if (NT_SUCCESS(Status)) {
  6643. //
  6644. // In the typical success case, don't call possibly an expensive
  6645. // routine to convert the error code.
  6646. //
  6647. ErrorCode = ERROR_SUCCESS;
  6648. } else {
  6649. ErrorCode = RtlNtStatusToDosError(Status);
  6650. }
  6651. return ErrorCode;
  6652. }
  6653. DWORD
  6654. PfSvGetFileIndexNumber(
  6655. WCHAR *FilePath,
  6656. PLARGE_INTEGER FileIndexNumber
  6657. )
  6658. /*++
  6659. Routine Description:
  6660. This routine queries the file system's IndexNumber for the specified
  6661. file.
  6662. Arguments:
  6663. FilePath - Pointer to full NT file path, e.g.
  6664. \Device\HarddiskVolume1\boot.ini, NOT Win32 path, e.g. c:\boot.ini
  6665. FileIndexNumber - If successful the index number is returned here.
  6666. Return Value:
  6667. Win32 error code.
  6668. --*/
  6669. {
  6670. HANDLE FileHandle;
  6671. BOOLEAN OpenedFile;
  6672. NTSTATUS Status;
  6673. OBJECT_ATTRIBUTES ObjectAttributes;
  6674. UNICODE_STRING FilePathU;
  6675. IO_STATUS_BLOCK IoStatusBlock;
  6676. FILE_INTERNAL_INFORMATION InternalInformation;
  6677. //
  6678. // Initialize locals.
  6679. //
  6680. OpenedFile = FALSE;
  6681. //
  6682. // Open the file.
  6683. //
  6684. RtlInitUnicodeString(&FilePathU, FilePath);
  6685. InitializeObjectAttributes(&ObjectAttributes,
  6686. &FilePathU,
  6687. OBJ_CASE_INSENSITIVE,
  6688. NULL,
  6689. NULL);
  6690. Status = NtCreateFile(&FileHandle,
  6691. STANDARD_RIGHTS_READ |
  6692. FILE_READ_ATTRIBUTES |
  6693. FILE_READ_EA,
  6694. &ObjectAttributes,
  6695. &IoStatusBlock,
  6696. 0,
  6697. 0,
  6698. FILE_SHARE_READ |
  6699. FILE_SHARE_WRITE |
  6700. FILE_SHARE_DELETE,
  6701. FILE_OPEN,
  6702. 0,
  6703. NULL,
  6704. 0);
  6705. if (!NT_SUCCESS(Status)) {
  6706. goto cleanup;
  6707. }
  6708. OpenedFile = TRUE;
  6709. //
  6710. // Query internal information.
  6711. //
  6712. Status = NtQueryInformationFile(FileHandle,
  6713. &IoStatusBlock,
  6714. &InternalInformation,
  6715. sizeof(InternalInformation),
  6716. FileInternalInformation);
  6717. if (!NT_SUCCESS(Status)) {
  6718. goto cleanup;
  6719. }
  6720. *FileIndexNumber = InternalInformation.IndexNumber;
  6721. Status = STATUS_SUCCESS;
  6722. cleanup:
  6723. if (OpenedFile) {
  6724. NtClose(FileHandle);
  6725. }
  6726. return RtlNtStatusToDosError(Status);
  6727. }
  6728. //
  6729. // String utility routines.
  6730. //
  6731. PFSV_SUFFIX_COMPARISON_RESULT
  6732. PfSvCompareSuffix(
  6733. WCHAR *String,
  6734. ULONG StringLength,
  6735. WCHAR *Suffix,
  6736. ULONG SuffixLength,
  6737. BOOLEAN CaseSensitive
  6738. )
  6739. /*++
  6740. Routine Description:
  6741. This compares the last characters of String to Suffix. The strings
  6742. don't have to be NUL terminated.
  6743. NOTE: The lexical ordering is done starting from the LAST
  6744. characters.
  6745. Arguments:
  6746. String - String to check suffix of.
  6747. StringLength - Number of characters in String.
  6748. Suffix - What the suffix of String should match.
  6749. SuffixLength - Number of characters in Suffix.
  6750. CaseSensitive - Whether the comparison should be case sensitive.
  6751. Return Value:
  6752. PFSV_SUFFIX_COMPARISON_RESULT
  6753. --*/
  6754. {
  6755. LONG StringCharIdx;
  6756. WCHAR StringChar;
  6757. LONG SuffixCharIdx;
  6758. WCHAR SuffixChar;
  6759. //
  6760. // If suffix is longer than the string itself, it cannot match.
  6761. //
  6762. if (SuffixLength > StringLength) {
  6763. return PfSvSuffixLongerThan;
  6764. }
  6765. //
  6766. // If the suffix is 0 length it matches anything.
  6767. //
  6768. if (SuffixLength == 0) {
  6769. return PfSvSuffixIdentical;
  6770. }
  6771. //
  6772. // If the suffix is not 0 length and it is greater than
  6773. // StringLength, StringLength cannot be 0.
  6774. //
  6775. PFSVC_ASSERT(StringLength);
  6776. //
  6777. // Start from the last character of the string and try to match
  6778. // the suffix.
  6779. //
  6780. StringCharIdx = StringLength - 1;
  6781. SuffixCharIdx = SuffixLength - 1;
  6782. while (SuffixCharIdx >= 0) {
  6783. SuffixChar = Suffix[SuffixCharIdx];
  6784. StringChar = String[StringCharIdx];
  6785. if (!CaseSensitive) {
  6786. SuffixChar = towupper(SuffixChar);
  6787. StringChar = towupper(StringChar);
  6788. }
  6789. //
  6790. // Is comparing the values of chars same comparing them
  6791. // lexically?
  6792. //
  6793. if (StringChar < SuffixChar) {
  6794. return PfSvSuffixGreaterThan;
  6795. } else if (StringChar > SuffixChar) {
  6796. return PfSvSuffixLessThan;
  6797. }
  6798. //
  6799. // Otherwise this character matches. Compare next one.
  6800. //
  6801. StringCharIdx--;
  6802. SuffixCharIdx--;
  6803. }
  6804. //
  6805. // All suffix characters matched.
  6806. //
  6807. return PfSvSuffixIdentical;
  6808. }
  6809. PFSV_PREFIX_COMPARISON_RESULT
  6810. PfSvComparePrefix(
  6811. WCHAR *String,
  6812. ULONG StringLength,
  6813. WCHAR *Prefix,
  6814. ULONG PrefixLength,
  6815. BOOLEAN CaseSensitive
  6816. )
  6817. /*++
  6818. Routine Description:
  6819. This compares the first characters of String to Prefix. The
  6820. strings don't have to be NUL terminated.
  6821. Arguments:
  6822. String - String to check prefix of.
  6823. StringLength - Number of characters in String.
  6824. Suffix - What the prefix of String should match.
  6825. SuffixLength - Number of characters in Prefix.
  6826. CaseSensitive - Whether the comparison should be case sensitive.
  6827. Return Value:
  6828. PFSV_PREFIX_COMPARISON_RESULT
  6829. --*/
  6830. {
  6831. LONG StrCmpResult;
  6832. //
  6833. // If prefix is longer than the string itself, it cannot match.
  6834. //
  6835. if (PrefixLength > StringLength) {
  6836. return PfSvPrefixLongerThan;
  6837. }
  6838. //
  6839. // If the prefix is 0 length it matches anything.
  6840. //
  6841. if (PrefixLength == 0) {
  6842. return PfSvPrefixIdentical;
  6843. }
  6844. //
  6845. // If the prefix is not 0 length and it is greater than
  6846. // StringLength, StringLength cannot be 0.
  6847. //
  6848. ASSERT(StringLength);
  6849. //
  6850. // Compare the prefix to the beginning of the string.
  6851. //
  6852. if (CaseSensitive) {
  6853. StrCmpResult = wcsncmp(Prefix, String, PrefixLength);
  6854. } else {
  6855. StrCmpResult = _wcsnicmp(Prefix, String, PrefixLength);
  6856. }
  6857. if (StrCmpResult == 0) {
  6858. return PfSvPrefixIdentical;
  6859. } else if (StrCmpResult > 0) {
  6860. return PfSvPrefixGreaterThan;
  6861. } else {
  6862. return PfSvPrefixLessThan;
  6863. }
  6864. }
  6865. VOID
  6866. FASTCALL
  6867. PfSvRemoveEndOfLineChars (
  6868. WCHAR *Line,
  6869. ULONG *LineLength
  6870. )
  6871. /*++
  6872. Routine Description:
  6873. If the Line ends with \n/\r\n, these characters are removed and
  6874. LineLength is adjusted accordingly.
  6875. Arguments:
  6876. Line - Pointer to line string.
  6877. LineLength - Pointer to length of line string in characters
  6878. excluding any terminating NULs. This is updated if carriage
  6879. return/linefeed characters are removed.
  6880. Return Value:
  6881. None.
  6882. --*/
  6883. {
  6884. if ((*LineLength) && (Line[(*LineLength) - 1] == L'\n')) {
  6885. Line[(*LineLength) - 1] = 0;
  6886. (*LineLength)--;
  6887. if ((*LineLength) && (Line[(*LineLength) - 1] == L'\r')) {
  6888. Line[(*LineLength) - 1] = 0;
  6889. (*LineLength)--;
  6890. }
  6891. }
  6892. }
  6893. PWCHAR
  6894. PfSvcAnsiToUnicode(
  6895. PCHAR str
  6896. )
  6897. /*++
  6898. Routine Description:
  6899. This routine converts an ANSI string into an allocated wide
  6900. character string. The returned string should be freed by
  6901. PfSvcFreeString.
  6902. Arguments:
  6903. str - Pointer to string to convert.
  6904. Return Value:
  6905. Allocated wide character string or NULL if there is a failure.
  6906. --*/
  6907. {
  6908. ULONG len;
  6909. wchar_t *retstr = NULL;
  6910. len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
  6911. retstr = (wchar_t *)PFSVC_ALLOC(len * sizeof(wchar_t));
  6912. if (!retstr)
  6913. {
  6914. return NULL;
  6915. }
  6916. MultiByteToWideChar(CP_ACP, 0, str, -1, retstr, len);
  6917. return retstr;
  6918. }
  6919. PCHAR
  6920. PfSvcUnicodeToAnsi(
  6921. PWCHAR wstr
  6922. )
  6923. /*++
  6924. Routine Description:
  6925. This routine converts a unicode string into an allocated ansi
  6926. string. The returned string should be freed by PfSvcFreeString.
  6927. Arguments:
  6928. wstr - Pointer to string to convert.
  6929. Return Value:
  6930. Allocated ANSI string or NULL if there is a failure.
  6931. --*/
  6932. {
  6933. ULONG len;
  6934. char *retstr = NULL;
  6935. len = WideCharToMultiByte(CP_ACP, 0, wstr, -1, NULL, 0, 0, 0);
  6936. retstr = (char *) PFSVC_ALLOC(len * sizeof(char));
  6937. if (!retstr)
  6938. {
  6939. return NULL;
  6940. }
  6941. WideCharToMultiByte(CP_ACP, 0, wstr, -1, retstr, len, 0, 0);
  6942. return retstr;
  6943. }
  6944. VOID
  6945. PfSvcFreeString(
  6946. PVOID String
  6947. )
  6948. /*++
  6949. Routine Description:
  6950. This routine frees a string allocated and returned by
  6951. PfSvcUnicodeToAnsi or PfSvcAnsiToUnicode.
  6952. Arguments:
  6953. String - Pointer to string to free.
  6954. Return Value:
  6955. None.
  6956. --*/
  6957. {
  6958. PFSVC_FREE(String);
  6959. }
  6960. //
  6961. // Routines that deal with information in the registry.
  6962. //
  6963. DWORD
  6964. PfSvSaveStartInfo (
  6965. HKEY ServiceDataKey
  6966. )
  6967. /*++
  6968. Routine Description:
  6969. This routine saves start time, prefetcher version etc. into the
  6970. registry.
  6971. Arguments:
  6972. ServiceDataKey - Key under which the values will be set.
  6973. Return Value:
  6974. Win32 error code.
  6975. --*/
  6976. {
  6977. DWORD ErrorCode;
  6978. DWORD PrefetchVersion;
  6979. SYSTEMTIME LocalTime;
  6980. WCHAR CurrentTime[50];
  6981. ULONG CurrentTimeMaxChars;
  6982. ULONG CurrentTimeSize;
  6983. //
  6984. // Initialize locals.
  6985. //
  6986. PrefetchVersion = PF_CURRENT_VERSION;
  6987. CurrentTimeMaxChars = sizeof(CurrentTime) / sizeof(WCHAR);
  6988. //
  6989. // Save version.
  6990. //
  6991. ErrorCode = RegSetValueEx(ServiceDataKey,
  6992. PFSVC_VERSION_VALUE_NAME,
  6993. 0,
  6994. REG_DWORD,
  6995. (PVOID) &PrefetchVersion,
  6996. sizeof(PrefetchVersion));
  6997. if (ErrorCode != ERROR_SUCCESS) {
  6998. goto cleanup;
  6999. }
  7000. //
  7001. // Get system time and convert it to a string.
  7002. //
  7003. GetLocalTime(&LocalTime);
  7004. _snwprintf(CurrentTime, CurrentTimeMaxChars,
  7005. L"%04d/%02d/%02d-%02d:%02d:%02d",
  7006. (ULONG)LocalTime.wYear,
  7007. (ULONG)LocalTime.wMonth,
  7008. (ULONG)LocalTime.wDay,
  7009. (ULONG)LocalTime.wHour,
  7010. (ULONG)LocalTime.wMinute,
  7011. (ULONG)LocalTime.wSecond);
  7012. //
  7013. // Make sure it is terminated.
  7014. //
  7015. CurrentTime[CurrentTimeMaxChars - 1] = 0;
  7016. //
  7017. // Save it to the registry.
  7018. //
  7019. CurrentTimeSize = (wcslen(CurrentTime) + 1) * sizeof(WCHAR);
  7020. ErrorCode = RegSetValueEx(ServiceDataKey,
  7021. PFSVC_START_TIME_VALUE_NAME,
  7022. 0,
  7023. REG_SZ,
  7024. (PVOID) CurrentTime,
  7025. CurrentTimeSize);
  7026. if (ErrorCode != ERROR_SUCCESS) {
  7027. goto cleanup;
  7028. }
  7029. //
  7030. // Save the initial statistics (which should be mostly zeros).
  7031. //
  7032. ErrorCode = PfSvSaveTraceProcessingStatistics(ServiceDataKey);
  7033. if (ErrorCode != ERROR_SUCCESS) {
  7034. goto cleanup;
  7035. }
  7036. ErrorCode = ERROR_SUCCESS;
  7037. cleanup:
  7038. return ErrorCode;
  7039. }
  7040. DWORD
  7041. PfSvSaveExitInfo (
  7042. HKEY ServiceDataKey,
  7043. DWORD ExitCode
  7044. )
  7045. /*++
  7046. Routine Description:
  7047. This routine saves the prefetcher service exit information to the
  7048. registry.
  7049. Arguments:
  7050. ServiceDataKey - Key under which the values will be set.
  7051. ExitCode - Win32 error code the service is exiting with.
  7052. Return Value:
  7053. Win32 error code.
  7054. --*/
  7055. {
  7056. DWORD ErrorCode;
  7057. SYSTEMTIME LocalTime;
  7058. WCHAR CurrentTime[50];
  7059. ULONG CurrentTimeMaxChars;
  7060. ULONG CurrentTimeSize;
  7061. //
  7062. // Initialize locals.
  7063. //
  7064. CurrentTimeMaxChars = sizeof(CurrentTime) / sizeof(WCHAR);
  7065. //
  7066. // Save exit code.
  7067. //
  7068. ErrorCode = RegSetValueEx(ServiceDataKey,
  7069. PFSVC_EXIT_CODE_VALUE_NAME,
  7070. 0,
  7071. REG_DWORD,
  7072. (PVOID) &ExitCode,
  7073. sizeof(ExitCode));
  7074. if (ErrorCode != ERROR_SUCCESS) {
  7075. goto cleanup;
  7076. }
  7077. //
  7078. // Get system time and convert it to a string.
  7079. //
  7080. GetLocalTime(&LocalTime);
  7081. _snwprintf(CurrentTime, CurrentTimeMaxChars,
  7082. L"%04d/%02d/%02d-%02d:%02d:%02d",
  7083. (ULONG)LocalTime.wYear,
  7084. (ULONG)LocalTime.wMonth,
  7085. (ULONG)LocalTime.wDay,
  7086. (ULONG)LocalTime.wHour,
  7087. (ULONG)LocalTime.wMinute,
  7088. (ULONG)LocalTime.wSecond);
  7089. //
  7090. // Make sure it is terminated.
  7091. //
  7092. CurrentTime[CurrentTimeMaxChars - 1] = 0;
  7093. //
  7094. // Save it to the registry.
  7095. //
  7096. CurrentTimeSize = (wcslen(CurrentTime) + 1) * sizeof(WCHAR);
  7097. ErrorCode = RegSetValueEx(ServiceDataKey,
  7098. PFSVC_EXIT_TIME_VALUE_NAME,
  7099. 0,
  7100. REG_SZ,
  7101. (PVOID) CurrentTime,
  7102. CurrentTimeSize);
  7103. if (ErrorCode != ERROR_SUCCESS) {
  7104. goto cleanup;
  7105. }
  7106. //
  7107. // Save the final statistics.
  7108. //
  7109. ErrorCode = PfSvSaveTraceProcessingStatistics(ServiceDataKey);
  7110. if (ErrorCode != ERROR_SUCCESS) {
  7111. goto cleanup;
  7112. }
  7113. ErrorCode = ERROR_SUCCESS;
  7114. cleanup:
  7115. return ErrorCode;
  7116. }
  7117. DWORD
  7118. PfSvSaveTraceProcessingStatistics (
  7119. HKEY ServiceDataKey
  7120. )
  7121. /*++
  7122. Routine Description:
  7123. This routine saves global trace processing statistics to the
  7124. registry.
  7125. Arguments:
  7126. ServiceDataKey - Key under which the values will be set.
  7127. Return Value:
  7128. Win32 error code.
  7129. --*/
  7130. {
  7131. DWORD ErrorCode;
  7132. //
  7133. // Save the various global statistics.
  7134. //
  7135. ErrorCode = RegSetValueEx(ServiceDataKey,
  7136. PFSVC_TRACES_PROCESSED_VALUE_NAME,
  7137. 0,
  7138. REG_DWORD,
  7139. (PVOID) &PfSvcGlobals.NumTracesProcessed,
  7140. sizeof(DWORD));
  7141. if (ErrorCode != ERROR_SUCCESS) {
  7142. goto cleanup;
  7143. }
  7144. ErrorCode = RegSetValueEx(ServiceDataKey,
  7145. PFSVC_TRACES_SUCCESSFUL_VALUE_NAME,
  7146. 0,
  7147. REG_DWORD,
  7148. (PVOID) &PfSvcGlobals.NumTracesSuccessful,
  7149. sizeof(DWORD));
  7150. if (ErrorCode != ERROR_SUCCESS) {
  7151. goto cleanup;
  7152. }
  7153. ErrorCode = RegSetValueEx(ServiceDataKey,
  7154. PFSVC_LAST_TRACE_FAILURE_VALUE_NAME,
  7155. 0,
  7156. REG_DWORD,
  7157. (PVOID) &PfSvcGlobals.LastTraceFailure,
  7158. sizeof(DWORD));
  7159. if (ErrorCode != ERROR_SUCCESS) {
  7160. goto cleanup;
  7161. }
  7162. ErrorCode = ERROR_SUCCESS;
  7163. cleanup:
  7164. return ErrorCode;
  7165. }
  7166. DWORD
  7167. PfSvGetLastDiskLayoutTime(
  7168. FILETIME *LastDiskLayoutTime
  7169. )
  7170. /*++
  7171. Routine Description:
  7172. This routine queries the last time disk layout was updated from
  7173. the registry under the service data key.
  7174. Arguments:
  7175. LastDiskLayoutTime - Pointer to output data.
  7176. Return Value:
  7177. Win32 error code.
  7178. --*/
  7179. {
  7180. ULONG Size;
  7181. DWORD ErrorCode;
  7182. DWORD RegValueType;
  7183. FILETIME CurrentFileTime;
  7184. SYSTEMTIME SystemTime;
  7185. //
  7186. // Query last disk layout time from the registry and adjust it if
  7187. // necessary.
  7188. //
  7189. Size = sizeof(FILETIME);
  7190. ErrorCode = RegQueryValueEx(PfSvcGlobals.ServiceDataKey,
  7191. PFSVC_LAST_DISK_LAYOUT_TIME_VALUE_NAME,
  7192. NULL,
  7193. &RegValueType,
  7194. (PVOID) LastDiskLayoutTime,
  7195. &Size);
  7196. if (ErrorCode != ERROR_SUCCESS) {
  7197. if (ErrorCode == ERROR_FILE_NOT_FOUND) {
  7198. //
  7199. // No successful runs of the defragger to update layout has
  7200. // been recorded in the registry.
  7201. //
  7202. RtlZeroMemory(LastDiskLayoutTime, sizeof(FILETIME));
  7203. } else {
  7204. //
  7205. // This is a real error.
  7206. //
  7207. goto cleanup;
  7208. }
  7209. } else {
  7210. //
  7211. // The query was successful, but if the value type is not
  7212. // REG_BINARY, we most likely read in trash.
  7213. //
  7214. if (RegValueType != REG_BINARY || (Size != sizeof(FILETIME))) {
  7215. RtlZeroMemory(LastDiskLayoutTime, sizeof(FILETIME));
  7216. } else {
  7217. //
  7218. // If the time we recorded looks greater than the current
  7219. // time (e.g. because the user played with the system time
  7220. // and such), adjust it.
  7221. //
  7222. GetSystemTime(&SystemTime);
  7223. if (!SystemTimeToFileTime(&SystemTime, &CurrentFileTime)) {
  7224. ErrorCode = GetLastError();
  7225. goto cleanup;
  7226. }
  7227. if (CompareFileTime(LastDiskLayoutTime, &CurrentFileTime) > 0) {
  7228. //
  7229. // The time in the registry looks bogus. We'll set it
  7230. // to 0, to drive our caller to run the defragger to
  7231. // update the layout again.
  7232. //
  7233. RtlZeroMemory(LastDiskLayoutTime, sizeof(FILETIME));
  7234. }
  7235. }
  7236. }
  7237. ErrorCode = ERROR_SUCCESS;
  7238. cleanup:
  7239. return ErrorCode;
  7240. }
  7241. DWORD
  7242. PfSvSetLastDiskLayoutTime(
  7243. FILETIME *LastDiskLayoutTime
  7244. )
  7245. /*++
  7246. Routine Description:
  7247. This routine saves the last time the disk layout was updated to
  7248. the registry under the service data key.
  7249. Arguments:
  7250. LastDiskLayoutTime - Pointer to new disk layout time.
  7251. Return Value:
  7252. Win32 error code.
  7253. --*/
  7254. {
  7255. DWORD ErrorCode;
  7256. WCHAR CurrentTime[50];
  7257. ULONG CurrentTimeMaxChars;
  7258. ULONG CurrentTimeSize;
  7259. FILETIME LocalFileTime;
  7260. SYSTEMTIME LocalSystemTime;
  7261. //
  7262. // Initialize locals.
  7263. //
  7264. CurrentTimeMaxChars = sizeof(CurrentTime) / sizeof(WCHAR);
  7265. //
  7266. // Save the specified time.
  7267. //
  7268. ErrorCode = RegSetValueEx(PfSvcGlobals.ServiceDataKey,
  7269. PFSVC_LAST_DISK_LAYOUT_TIME_VALUE_NAME,
  7270. 0,
  7271. REG_BINARY,
  7272. (PVOID) LastDiskLayoutTime,
  7273. sizeof(FILETIME));
  7274. if (ErrorCode != ERROR_SUCCESS) {
  7275. goto cleanup;
  7276. }
  7277. //
  7278. // Also save it in human readable format.
  7279. //
  7280. if (!FileTimeToLocalFileTime(LastDiskLayoutTime, &LocalFileTime)) {
  7281. ErrorCode = GetLastError();
  7282. goto cleanup;
  7283. }
  7284. if (!FileTimeToSystemTime(&LocalFileTime, &LocalSystemTime)) {
  7285. ErrorCode = GetLastError();
  7286. goto cleanup;
  7287. }
  7288. _snwprintf(CurrentTime, CurrentTimeMaxChars,
  7289. L"%04d/%02d/%02d-%02d:%02d:%02d",
  7290. (ULONG)LocalSystemTime.wYear,
  7291. (ULONG)LocalSystemTime.wMonth,
  7292. (ULONG)LocalSystemTime.wDay,
  7293. (ULONG)LocalSystemTime.wHour,
  7294. (ULONG)LocalSystemTime.wMinute,
  7295. (ULONG)LocalSystemTime.wSecond);
  7296. //
  7297. // Make sure it is terminated.
  7298. //
  7299. CurrentTime[CurrentTimeMaxChars - 1] = 0;
  7300. //
  7301. // Save it to the registry.
  7302. //
  7303. CurrentTimeSize = (wcslen(CurrentTime) + 1) * sizeof(WCHAR);
  7304. ErrorCode = RegSetValueEx(PfSvcGlobals.ServiceDataKey,
  7305. PFSVC_LAST_DISK_LAYOUT_TIME_STRING_VALUE_NAME,
  7306. 0,
  7307. REG_SZ,
  7308. (PVOID) CurrentTime,
  7309. CurrentTimeSize);
  7310. if (ErrorCode != ERROR_SUCCESS) {
  7311. goto cleanup;
  7312. }
  7313. ErrorCode = ERROR_SUCCESS;
  7314. cleanup:
  7315. return ErrorCode;
  7316. }
  7317. DWORD
  7318. PfSvGetDontRunDefragger(
  7319. DWORD *DontRunDefragger
  7320. )
  7321. /*++
  7322. Routine Description:
  7323. This routine queries the registry setting that disables launching
  7324. the defragger when the system is idle.
  7325. Arguments:
  7326. DontRunDefragger - Pointer to output data.
  7327. Return Value:
  7328. Win32 error code.
  7329. --*/
  7330. {
  7331. HKEY ParametersKey;
  7332. ULONG Size;
  7333. DWORD Value;
  7334. DWORD ErrorCode;
  7335. DWORD RegValueType;
  7336. BOOLEAN OpenedParametersKey;
  7337. //
  7338. // Initialize locals.
  7339. //
  7340. OpenedParametersKey = FALSE;
  7341. //
  7342. // Open the parameters key, creating it if necessary.
  7343. //
  7344. ErrorCode = RegCreateKey(HKEY_LOCAL_MACHINE,
  7345. PFSVC_OPTIMAL_LAYOUT_REG_KEY_PATH,
  7346. &ParametersKey);
  7347. if (ErrorCode != ERROR_SUCCESS) {
  7348. goto cleanup;
  7349. }
  7350. OpenedParametersKey = TRUE;
  7351. //
  7352. // Query whether auto layout is enabled.
  7353. //
  7354. Size = sizeof(Value);
  7355. ErrorCode = RegQueryValueEx(ParametersKey,
  7356. PFSVC_OPTIMAL_LAYOUT_ENABLE_VALUE_NAME,
  7357. NULL,
  7358. &RegValueType,
  7359. (PVOID) &Value,
  7360. &Size);
  7361. if (ErrorCode != ERROR_SUCCESS) {
  7362. goto cleanup;
  7363. }
  7364. //
  7365. // The query was successful. Make sure value is a DWORD.
  7366. //
  7367. if (RegValueType != REG_DWORD) {
  7368. ErrorCode = ERROR_BAD_FORMAT;
  7369. goto cleanup;
  7370. }
  7371. //
  7372. // Set the value.
  7373. //
  7374. *DontRunDefragger = !(Value);
  7375. ErrorCode = ERROR_SUCCESS;
  7376. cleanup:
  7377. if (OpenedParametersKey) {
  7378. CloseHandle(ParametersKey);
  7379. }
  7380. return ErrorCode;
  7381. }
  7382. BOOLEAN
  7383. PfSvAllowedToRunDefragger(
  7384. BOOLEAN CheckRegistry
  7385. )
  7386. /*++
  7387. Routine Description:
  7388. This routine checks the global state/parameters to see if we
  7389. are allowed to try to run the defragger.
  7390. Arguments:
  7391. CheckRegistry - Whether to ignore auto-layout enable key in the registry.
  7392. Return Value:
  7393. TRUE - Go ahead and run the defragger.
  7394. FALSE - Don't run the defragger.
  7395. --*/
  7396. {
  7397. PF_SCENARIO_TYPE ScenarioType;
  7398. BOOLEAN AllowedToRunDefragger;
  7399. BOOLEAN PrefetchingEnabled;
  7400. //
  7401. // Initialize locals.
  7402. //
  7403. AllowedToRunDefragger = FALSE;
  7404. //
  7405. // On checked builds allow auto-layout on other sku's too.
  7406. //
  7407. #ifndef PFSVC_DBG
  7408. //
  7409. // Is this a server machine?
  7410. //
  7411. if (PfSvcGlobals.OsVersion.wProductType != VER_NT_WORKSTATION) {
  7412. goto cleanup;
  7413. }
  7414. #endif
  7415. //
  7416. // Is prefetching enabled for any scenario type?
  7417. //
  7418. PrefetchingEnabled = FALSE;
  7419. for(ScenarioType = 0; ScenarioType < PfMaxScenarioType; ScenarioType++) {
  7420. if (PfSvcGlobals.Parameters.EnableStatus[ScenarioType] == PfSvEnabled) {
  7421. PrefetchingEnabled = TRUE;
  7422. break;
  7423. }
  7424. }
  7425. if (!PrefetchingEnabled) {
  7426. goto cleanup;
  7427. }
  7428. //
  7429. // Did we try to run the defragger and it crashed before?
  7430. //
  7431. if (PfSvcGlobals.DefraggerErrorCode != ERROR_SUCCESS) {
  7432. goto cleanup;
  7433. }
  7434. //
  7435. // If in the registry we were not allowed to run the defragger, don't
  7436. // do so.
  7437. //
  7438. if (CheckRegistry) {
  7439. if (PfSvcGlobals.DontRunDefragger) {
  7440. goto cleanup;
  7441. }
  7442. }
  7443. //
  7444. // If we passed all checks, we are allowed to run the defragger.
  7445. //
  7446. AllowedToRunDefragger = TRUE;
  7447. cleanup:
  7448. return AllowedToRunDefragger;
  7449. }
  7450. //
  7451. // Routines that deal with security.
  7452. //
  7453. BOOL
  7454. PfSvSetPrivilege(
  7455. HANDLE hToken,
  7456. LPCTSTR lpszPrivilege,
  7457. ULONG ulPrivilege,
  7458. BOOL bEnablePrivilege
  7459. )
  7460. /*++
  7461. Routine Description:
  7462. Enables or disables a privilege in an access token.
  7463. Arguments:
  7464. hToken - Access token handle.
  7465. lpszPrivilege - Name of privilege to enable/disable.
  7466. ulPrivilege - If a name is not specified, then a ULONG privilege
  7467. should be specified.
  7468. bEnablePrivilege - Whether to enable or disable privilege
  7469. Return Value:
  7470. TRUE - Success.
  7471. FALSE - Failure.
  7472. --*/
  7473. {
  7474. TOKEN_PRIVILEGES tp;
  7475. LUID luid;
  7476. if (lpszPrivilege) {
  7477. if ( !LookupPrivilegeValue(NULL,
  7478. lpszPrivilege,
  7479. &luid)) {
  7480. return FALSE;
  7481. }
  7482. } else {
  7483. luid = RtlConvertUlongToLuid(ulPrivilege);
  7484. }
  7485. tp.PrivilegeCount = 1;
  7486. tp.Privileges[0].Luid = luid;
  7487. if (bEnablePrivilege)
  7488. tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
  7489. else
  7490. tp.Privileges[0].Attributes = 0;
  7491. //
  7492. // Enable the privilege or disable all privileges.
  7493. //
  7494. AdjustTokenPrivileges(
  7495. hToken,
  7496. FALSE,
  7497. &tp,
  7498. 0,
  7499. (PTOKEN_PRIVILEGES) NULL,
  7500. (PDWORD) NULL);
  7501. //
  7502. // Call GetLastError to determine whether the function succeeded.
  7503. //
  7504. if (GetLastError() != ERROR_SUCCESS) {
  7505. return FALSE;
  7506. }
  7507. return TRUE;
  7508. }
  7509. DWORD
  7510. PfSvSetAdminOnlyPermissions(
  7511. WCHAR *ObjectPath,
  7512. HANDLE ObjectHandle,
  7513. SE_OBJECT_TYPE ObjectType
  7514. )
  7515. /*++
  7516. Routine Description:
  7517. This routine makes the built-in administrators group the owner and
  7518. only allowed in the DACL of the specified directory or event
  7519. object.
  7520. The calling thread must have the SE_TAKE_OWNERSHIP_NAME privilege.
  7521. Arguments:
  7522. ObjectPath - File/directory path or event name.
  7523. ObjectHandle - If this is a SE_KERNEL_OBJECT, handle to it,
  7524. otherwise NULL.
  7525. ObjectType - Security object type. Only SE_KERNEL_OBJECT and
  7526. SE_FILE_OBJECT are supported.
  7527. Return Value:
  7528. Win32 error code.
  7529. --*/
  7530. {
  7531. DWORD ErrorCode;
  7532. SID_IDENTIFIER_AUTHORITY SIDAuthority = SECURITY_NT_AUTHORITY;
  7533. PSID AdministratorsSID;
  7534. PSECURITY_DESCRIPTOR SecurityDescriptor;
  7535. PACL DiscretionaryACL;
  7536. DWORD ACLRevision;
  7537. ULONG ACESize;
  7538. ULONG ACLSize;
  7539. PACCESS_ALLOWED_ACE AccessAllowedAce;
  7540. BOOL Result;
  7541. //
  7542. // Initialize locals.
  7543. //
  7544. AdministratorsSID = NULL;
  7545. SecurityDescriptor = NULL;
  7546. DiscretionaryACL = NULL;
  7547. ACLRevision = ACL_REVISION;
  7548. //
  7549. // Check parameters.
  7550. //
  7551. if (ObjectType == SE_KERNEL_OBJECT) {
  7552. if (ObjectHandle == NULL) {
  7553. ErrorCode = ERROR_INVALID_PARAMETER;
  7554. goto cleanup;
  7555. }
  7556. } else if (ObjectType == SE_FILE_OBJECT) {
  7557. if (ObjectHandle != NULL) {
  7558. ErrorCode = ERROR_INVALID_PARAMETER;
  7559. goto cleanup;
  7560. }
  7561. } else {
  7562. ErrorCode = ERROR_INVALID_PARAMETER;
  7563. goto cleanup;
  7564. }
  7565. //
  7566. // Create a SID for the BUILTIN\Administrators group.
  7567. //
  7568. if(!AllocateAndInitializeSid(&SIDAuthority,
  7569. 2,
  7570. SECURITY_BUILTIN_DOMAIN_RID,
  7571. DOMAIN_ALIAS_RID_ADMINS,
  7572. 0, 0, 0, 0, 0, 0,
  7573. &AdministratorsSID)) {
  7574. ErrorCode = GetLastError();
  7575. goto cleanup;
  7576. }
  7577. //
  7578. // Make Administrators the owner.
  7579. //
  7580. ErrorCode = SetNamedSecurityInfo (ObjectPath,
  7581. ObjectType,
  7582. OWNER_SECURITY_INFORMATION,
  7583. AdministratorsSID,
  7584. NULL,
  7585. NULL,
  7586. NULL);
  7587. if (ErrorCode != ERROR_SUCCESS) {
  7588. goto cleanup;
  7589. }
  7590. //
  7591. // Setup a discretionary access control list:
  7592. //
  7593. //
  7594. // Determine size of an ACCESS_ALLOWED access control entry for
  7595. // the administrators group. (Subtract size of SidStart which is
  7596. // both part of ACE and SID.
  7597. //
  7598. ACESize = sizeof(ACCESS_ALLOWED_ACE);
  7599. ACESize -= sizeof (AccessAllowedAce->SidStart);
  7600. ACESize += GetLengthSid(AdministratorsSID);
  7601. //
  7602. // Determine size of the access control list.
  7603. //
  7604. ACLSize = sizeof(ACL);
  7605. ACLSize += ACESize;
  7606. //
  7607. // Allocate and initialize the access control list.
  7608. //
  7609. DiscretionaryACL = PFSVC_ALLOC(ACLSize);
  7610. if (!DiscretionaryACL) {
  7611. ErrorCode = ERROR_NOT_ENOUGH_MEMORY;
  7612. goto cleanup;
  7613. }
  7614. if (!InitializeAcl(DiscretionaryACL, ACLSize, ACLRevision)) {
  7615. ErrorCode = GetLastError();
  7616. goto cleanup;
  7617. }
  7618. //
  7619. // Add an ACE to allow access for the Administrators group.
  7620. //
  7621. if (!AddAccessAllowedAce(DiscretionaryACL,
  7622. ACLRevision,
  7623. GENERIC_ALL,
  7624. AdministratorsSID)) {
  7625. ErrorCode = GetLastError();
  7626. goto cleanup;
  7627. }
  7628. //
  7629. // Initialize a security descriptor.
  7630. //
  7631. SecurityDescriptor = PFSVC_ALLOC(SECURITY_DESCRIPTOR_MIN_LENGTH);
  7632. if (!SecurityDescriptor) {
  7633. ErrorCode = ERROR_NOT_ENOUGH_MEMORY;
  7634. goto cleanup;
  7635. }
  7636. if (!InitializeSecurityDescriptor(SecurityDescriptor,
  7637. SECURITY_DESCRIPTOR_REVISION)) {
  7638. ErrorCode = GetLastError();
  7639. goto cleanup;
  7640. }
  7641. //
  7642. // Set the discretionary access control list on the security descriptor.
  7643. //
  7644. if (!SetSecurityDescriptorDacl(SecurityDescriptor,
  7645. TRUE,
  7646. DiscretionaryACL,
  7647. FALSE)) {
  7648. ErrorCode = GetLastError();
  7649. goto cleanup;
  7650. }
  7651. //
  7652. // Set the built security descriptor on the prefetch directory.
  7653. //
  7654. if (ObjectType == SE_FILE_OBJECT) {
  7655. Result = SetFileSecurity(ObjectPath,
  7656. DACL_SECURITY_INFORMATION,
  7657. SecurityDescriptor);
  7658. } else {
  7659. PFSVC_ASSERT(ObjectType == SE_KERNEL_OBJECT);
  7660. PFSVC_ASSERT(ObjectHandle);
  7661. Result = SetKernelObjectSecurity(ObjectHandle,
  7662. DACL_SECURITY_INFORMATION,
  7663. SecurityDescriptor);
  7664. }
  7665. if (!Result) {
  7666. ErrorCode = GetLastError();
  7667. goto cleanup;
  7668. }
  7669. //
  7670. // We are done.
  7671. //
  7672. ErrorCode = ERROR_SUCCESS;
  7673. cleanup:
  7674. if (AdministratorsSID) {
  7675. FreeSid(AdministratorsSID);
  7676. }
  7677. if (SecurityDescriptor) {
  7678. PFSVC_FREE(SecurityDescriptor);
  7679. }
  7680. if (DiscretionaryACL) {
  7681. PFSVC_FREE(DiscretionaryACL);
  7682. }
  7683. return ErrorCode;
  7684. }
  7685. DWORD
  7686. PfSvGetPrefetchServiceThreadPrivileges (
  7687. VOID
  7688. )
  7689. /*++
  7690. Routine Description:
  7691. This routine ensures there is a security token for the current
  7692. thread and sets the right privileges on it so the thread can
  7693. communicate with the kernel mode prefetcher. It should be called
  7694. right after a thread is created.
  7695. Arguments:
  7696. None.
  7697. Return Value:
  7698. Win32 error code.
  7699. --*/
  7700. {
  7701. DWORD ErrorCode;
  7702. BOOLEAN ImpersonatedSelf;
  7703. BOOLEAN OpenedThreadToken;
  7704. HANDLE ThreadToken;
  7705. //
  7706. // Initialize locals.
  7707. //
  7708. ImpersonatedSelf = FALSE;
  7709. OpenedThreadToken = FALSE;
  7710. DBGPR((PFID,PFTRC,"PFSVC: GetThreadPriviliges()\n"));
  7711. //
  7712. // Obtain a security context for this thread so we can set
  7713. // privileges etc. without effecting the whole process.
  7714. //
  7715. if (!ImpersonateSelf(SecurityImpersonation)) {
  7716. DBGPR((PFID,PFERR,"PFSVC: GetThreadPriviliges()-FailedImpersonateSelf\n"));
  7717. ErrorCode = GetLastError();
  7718. goto cleanup;
  7719. }
  7720. ImpersonatedSelf = TRUE;
  7721. //
  7722. // Set the privileges we will need talk to the kernel mode
  7723. // prefetcher:
  7724. //
  7725. //
  7726. // Open thread's access token.
  7727. //
  7728. if (!OpenThreadToken(GetCurrentThread(),
  7729. TOKEN_ADJUST_PRIVILEGES,
  7730. FALSE,
  7731. &ThreadToken)) {
  7732. DBGPR((PFID,PFERR,"PFSVC: GetThreadPriviliges()-FailedOpenToken\n"));
  7733. ErrorCode = GetLastError();
  7734. goto cleanup;
  7735. }
  7736. OpenedThreadToken = TRUE;
  7737. //
  7738. // Enable the SE_PROF_SINGLE_PROCESS_PRIVILEGE privilege so the
  7739. // kernel mode prefetcher accepts our queries & set requests.
  7740. //
  7741. //
  7742. if (!PfSvSetPrivilege(ThreadToken, 0, SE_PROF_SINGLE_PROCESS_PRIVILEGE, TRUE)) {
  7743. DBGPR((PFID,PFERR,"PFSVC: GetThreadPriviliges()-FailedEnableProf\n"));
  7744. ErrorCode = GetLastError();
  7745. goto cleanup;
  7746. }
  7747. //
  7748. // Enable the SE_TAKE_OWNERSHIP_NAME privilege so we can get
  7749. // ownership of the prefetch directory.
  7750. //
  7751. if (!PfSvSetPrivilege(ThreadToken, SE_TAKE_OWNERSHIP_NAME, 0, TRUE)) {
  7752. DBGPR((PFID,PFERR,"PFSVC: GetThreadPriviliges()-FailedEnableOwn\n"));
  7753. ErrorCode = GetLastError();
  7754. goto cleanup;
  7755. }
  7756. ErrorCode = ERROR_SUCCESS;
  7757. cleanup:
  7758. if (OpenedThreadToken) {
  7759. CloseHandle(ThreadToken);
  7760. }
  7761. if (ErrorCode != ERROR_SUCCESS) {
  7762. if (ImpersonatedSelf) {
  7763. RevertToSelf();
  7764. }
  7765. }
  7766. DBGPR((PFID,PFTRC,"PFSVC: GetThreadPriviliges()=%x\n",ErrorCode));
  7767. return ErrorCode;
  7768. }
  7769. //
  7770. // Routines that deal with volume node structures.
  7771. //
  7772. DWORD
  7773. PfSvCreateVolumeNode (
  7774. PPFSVC_SCENARIO_INFO ScenarioInfo,
  7775. WCHAR *VolumePath,
  7776. ULONG VolumePathLength,
  7777. PLARGE_INTEGER CreationTime,
  7778. ULONG SerialNumber
  7779. )
  7780. /*++
  7781. Routine Description:
  7782. This routine creates a volume node with the specifed info if it
  7783. does not already exist. If a node already exists, it verifies
  7784. CreationTime and SerialNumber.
  7785. Arguments:
  7786. ScenarioInfo - Pointer to new scenario information.
  7787. VolumePath - UPCASE NT full path of the volume, NUL terminated.
  7788. VolumePathLength - Number of characters in VolumePath excluding NUL.
  7789. CreationTime & SerialNumber - For the volume.
  7790. Return Value:
  7791. ERROR_REVISION_MISMATCH - There already exists a volume node with
  7792. that path but with a different signature/creation time.
  7793. Win32 error code.
  7794. --*/
  7795. {
  7796. PLIST_ENTRY HeadEntry;
  7797. PLIST_ENTRY NextEntry;
  7798. PLIST_ENTRY FoundPosition;
  7799. PPFSVC_VOLUME_NODE VolumeNode;
  7800. DWORD ErrorCode;
  7801. LONG ComparisonResult;
  7802. PPFSVC_VOLUME_NODE NewVolumeNode;
  7803. PWCHAR NewVolumePath;
  7804. //
  7805. // Initialize locals.
  7806. //
  7807. NewVolumeNode = NULL;
  7808. NewVolumePath = NULL;
  7809. DBGPR((PFID,PFSTRC,"PFSVC: CreateVolumeNode(%ws)\n", VolumePath));
  7810. //
  7811. // Walk through the existing volume nodes list and try to find
  7812. // matching one.
  7813. //
  7814. HeadEntry = &ScenarioInfo->VolumeList;
  7815. NextEntry = HeadEntry->Flink;
  7816. FoundPosition = NULL;
  7817. while (NextEntry != HeadEntry) {
  7818. VolumeNode = CONTAINING_RECORD(NextEntry,
  7819. PFSVC_VOLUME_NODE,
  7820. VolumeLink);
  7821. NextEntry = NextEntry->Flink;
  7822. ComparisonResult = wcsncmp(VolumePath,
  7823. VolumeNode->VolumePath,
  7824. VolumePathLength);
  7825. if (ComparisonResult == 0) {
  7826. //
  7827. // Make sure VolumePathLengths are equal.
  7828. //
  7829. if (VolumeNode->VolumePathLength != VolumePathLength) {
  7830. //
  7831. // Continue searching.
  7832. //
  7833. continue;
  7834. }
  7835. //
  7836. // We found our volume. Verify magics.
  7837. //
  7838. if (VolumeNode->SerialNumber != SerialNumber ||
  7839. VolumeNode->CreationTime.QuadPart != CreationTime->QuadPart) {
  7840. ErrorCode = ERROR_REVISION_MISMATCH;
  7841. goto cleanup;
  7842. } else {
  7843. ErrorCode = ERROR_SUCCESS;
  7844. goto cleanup;
  7845. }
  7846. } else if (ComparisonResult < 0) {
  7847. //
  7848. // The volume paths are sorted lexically. The file path
  7849. // would be less than other volumes too. The new node
  7850. // would go right before this node.
  7851. //
  7852. FoundPosition = &VolumeNode->VolumeLink;
  7853. break;
  7854. }
  7855. //
  7856. // Continue looking...
  7857. //
  7858. }
  7859. //
  7860. // If we could not find an entry to put the new entry before, it
  7861. // goes before the list head.
  7862. //
  7863. if (!FoundPosition) {
  7864. FoundPosition = HeadEntry;
  7865. }
  7866. //
  7867. // If we break out of the while loop, we could not find a
  7868. // volume. We should create a new node.
  7869. //
  7870. NewVolumeNode = PfSvChunkAllocatorAllocate(&ScenarioInfo->VolumeNodeAllocator);
  7871. if (!NewVolumeNode) {
  7872. ErrorCode = ERROR_NOT_ENOUGH_MEMORY;
  7873. goto cleanup;
  7874. }
  7875. NewVolumePath = PfSvStringAllocatorAllocate(&ScenarioInfo->PathAllocator,
  7876. (VolumePathLength + 1) * sizeof(WCHAR));
  7877. if (!NewVolumePath) {
  7878. ErrorCode = ERROR_NOT_ENOUGH_MEMORY;
  7879. goto cleanup;
  7880. }
  7881. //
  7882. // Copy file's volume path.
  7883. //
  7884. wcsncpy(NewVolumePath, VolumePath, VolumePathLength);
  7885. NewVolumePath[VolumePathLength] = 0;
  7886. //
  7887. // Initialize volume node.
  7888. //
  7889. NewVolumeNode->VolumePath = NewVolumePath;
  7890. NewVolumeNode->VolumePathLength = VolumePathLength;
  7891. NewVolumeNode->SerialNumber = SerialNumber;
  7892. NewVolumeNode->CreationTime = (*CreationTime);
  7893. InitializeListHead(&NewVolumeNode->SectionList);
  7894. PfSvInitializePathList(&NewVolumeNode->DirectoryList, &ScenarioInfo->PathAllocator, TRUE);
  7895. NewVolumeNode->NumSections = 0;
  7896. NewVolumeNode->NumAllSections = 0;
  7897. NewVolumeNode->MFTSectionNode = NULL;
  7898. //
  7899. // Add it to the scenario's volume list before the found position.
  7900. //
  7901. InsertTailList(FoundPosition, &NewVolumeNode->VolumeLink);
  7902. ErrorCode = ERROR_SUCCESS;
  7903. cleanup:
  7904. if (ErrorCode != ERROR_SUCCESS) {
  7905. if (NewVolumePath) {
  7906. PfSvStringAllocatorFree(&ScenarioInfo->PathAllocator, NewVolumePath);
  7907. }
  7908. if (NewVolumeNode) {
  7909. PfSvChunkAllocatorFree(&ScenarioInfo->VolumeNodeAllocator, NewVolumeNode);
  7910. }
  7911. }
  7912. DBGPR((PFID,PFSTRC,"PFSVC: CreateVolumeNode()=%x\n", ErrorCode));
  7913. return ErrorCode;
  7914. }
  7915. PPFSVC_VOLUME_NODE
  7916. PfSvGetVolumeNode (
  7917. PPFSVC_SCENARIO_INFO ScenarioInfo,
  7918. WCHAR *FilePath,
  7919. ULONG FilePathLength
  7920. )
  7921. /*++
  7922. Routine Description:
  7923. This routine looks for a volume node for the specified file path.
  7924. Arguments:
  7925. ScenarioInfo - Pointer to new scenario information.
  7926. FilePath - NT full path of the file, NUL terminated.
  7927. FilePathLength - Number of characters in FilePath excluding NUL.
  7928. Return Value:
  7929. Pointer to found VolumeNode, or NULL.
  7930. --*/
  7931. {
  7932. PLIST_ENTRY HeadEntry;
  7933. PLIST_ENTRY NextEntry;
  7934. PPFSVC_VOLUME_NODE VolumeNode;
  7935. DWORD ErrorCode;
  7936. PFSV_PREFIX_COMPARISON_RESULT ComparisonResult;
  7937. DBGPR((PFID,PFSTRC,"PFSVC: GetVolumeNode(%ws)\n", FilePath));
  7938. //
  7939. // Walk through the existing volume nodes list and try to find
  7940. // matching one.
  7941. //
  7942. HeadEntry = &ScenarioInfo->VolumeList;
  7943. NextEntry = HeadEntry->Flink;
  7944. while (NextEntry != HeadEntry) {
  7945. VolumeNode = CONTAINING_RECORD(NextEntry,
  7946. PFSVC_VOLUME_NODE,
  7947. VolumeLink);
  7948. NextEntry = NextEntry->Flink;
  7949. ComparisonResult = PfSvComparePrefix(FilePath,
  7950. FilePathLength,
  7951. VolumeNode->VolumePath,
  7952. VolumeNode->VolumePathLength,
  7953. TRUE);
  7954. if (ComparisonResult == PfSvPrefixIdentical) {
  7955. //
  7956. // Make sure that there is a slash in the file
  7957. // path after the volume path.
  7958. //
  7959. if (FilePath[VolumeNode->VolumePathLength] != L'\\') {
  7960. //
  7961. // Continue searching.
  7962. //
  7963. continue;
  7964. }
  7965. //
  7966. // We found our volume.
  7967. //
  7968. ErrorCode = ERROR_SUCCESS;
  7969. goto cleanup;
  7970. } else if (ComparisonResult == PfSvPrefixGreaterThan) {
  7971. //
  7972. // The volume paths are sorted lexically. The volume path
  7973. // would be less than other volumes too.
  7974. //
  7975. break;
  7976. }
  7977. //
  7978. // Continue looking...
  7979. //
  7980. }
  7981. //
  7982. // If we break out of the while loop, we could not find a
  7983. // volume.
  7984. //
  7985. VolumeNode = NULL;
  7986. ErrorCode = ERROR_NOT_FOUND;
  7987. cleanup:
  7988. if (ErrorCode != ERROR_SUCCESS) {
  7989. VolumeNode = NULL;
  7990. }
  7991. DBGPR((PFID,PFSTRC,"PFSVC: GetVolumeNode()=%p\n", VolumeNode));
  7992. return VolumeNode;
  7993. }
  7994. VOID
  7995. PfSvCleanupVolumeNode(
  7996. PPFSVC_SCENARIO_INFO ScenarioInfo,
  7997. PPFSVC_VOLUME_NODE VolumeNode
  7998. )
  7999. /*++
  8000. Routine Description:
  8001. This function cleans up a volume node structure. It does not free
  8002. the structure itself.
  8003. Arguments:
  8004. ScenarioInfo - Pointer to scenario info context this volume node
  8005. belongs to.
  8006. VolumeNode - Pointer to structure.
  8007. Return Value:
  8008. None.
  8009. --*/
  8010. {
  8011. PLIST_ENTRY SectListEntry;
  8012. PPFSVC_SECTION_NODE SectionNode;
  8013. //
  8014. // Cleanup directory list.
  8015. //
  8016. PfSvCleanupPathList(&VolumeNode->DirectoryList);
  8017. //
  8018. // If there is a volume path, free it.
  8019. //
  8020. if (VolumeNode->VolumePath) {
  8021. PfSvStringAllocatorFree(&ScenarioInfo->PathAllocator, VolumeNode->VolumePath);
  8022. VolumeNode->VolumePath = NULL;
  8023. }
  8024. //
  8025. // Remove the section nodes from our list and re-initialize their
  8026. // links so they know they have been removed.
  8027. //
  8028. while (!IsListEmpty(&VolumeNode->SectionList)) {
  8029. SectListEntry = RemoveHeadList(&VolumeNode->SectionList);
  8030. SectionNode = CONTAINING_RECORD(SectListEntry,
  8031. PFSVC_SECTION_NODE,
  8032. SectionVolumeLink);
  8033. InitializeListHead(&SectionNode->SectionVolumeLink);
  8034. }
  8035. return;
  8036. }
  8037. DWORD
  8038. PfSvAddParentDirectoriesToList(
  8039. PPFSVC_PATH_LIST DirectoryList,
  8040. ULONG VolumePathLength,
  8041. WCHAR *FilePath,
  8042. ULONG FilePathLength
  8043. )
  8044. /*++
  8045. Routine Description:
  8046. This function will parse a fully qualified NT file path and add
  8047. all parent directories to the specified directory list. The part
  8048. of the path that is the volume path is skipped.
  8049. Arguments:
  8050. DirectoryList - Pointer to list to update.
  8051. VolumePathLength - Position in the file path at which the volume
  8052. path ends and the root directory starts.
  8053. FilePath - Pointer to NT file path, NUL terminated.
  8054. FullPathLength - Length of FilePath in characters excluding NUL.
  8055. Return Value:
  8056. Win32 error code.
  8057. --*/
  8058. {
  8059. DWORD ErrorCode;
  8060. WCHAR DirectoryPath[MAX_PATH];
  8061. ULONG DirectoryPathLength;
  8062. WCHAR *CurrentChar;
  8063. WCHAR *FilePathEnd;
  8064. //
  8065. // Initialize locals.
  8066. //
  8067. FilePathEnd = FilePath + FilePathLength;
  8068. PFSVC_ASSERT(*FilePathEnd == 0);
  8069. //
  8070. // Skip the volume path and start from the root directory.
  8071. //
  8072. CurrentChar = FilePath + VolumePathLength;
  8073. while (CurrentChar < FilePathEnd) {
  8074. if (*CurrentChar == L'\\') {
  8075. //
  8076. // We got a directory.
  8077. //
  8078. DirectoryPathLength = (ULONG) (CurrentChar - FilePath + 1);
  8079. if (DirectoryPathLength >= MAX_PATH) {
  8080. ErrorCode = ERROR_INSUFFICIENT_BUFFER;
  8081. goto cleanup;
  8082. }
  8083. //
  8084. // Copy directory path to buffer and NUL terminate it.
  8085. //
  8086. wcsncpy(DirectoryPath, FilePath, DirectoryPathLength);
  8087. DirectoryPath[DirectoryPathLength] = 0;
  8088. PFSVC_ASSERT(DirectoryPath[DirectoryPathLength - 1] == L'\\');
  8089. //
  8090. // Add it to the list.
  8091. //
  8092. ErrorCode = PfSvAddToPathList(DirectoryList,
  8093. DirectoryPath,
  8094. DirectoryPathLength);
  8095. if (ErrorCode != ERROR_SUCCESS) {
  8096. goto cleanup;
  8097. }
  8098. //
  8099. // Continue looking for more directories in the path.
  8100. //
  8101. }
  8102. CurrentChar++;
  8103. }
  8104. ErrorCode = ERROR_SUCCESS;
  8105. cleanup:
  8106. return ErrorCode;
  8107. }
  8108. //
  8109. // Routines used to allocate / free section & page nodes etc. efficiently.
  8110. //
  8111. // FUTURE-2002/03/29-ScottMa -- The "optional" Buffer parameter is always
  8112. // supplied in both the chunk and string allocators. You could remove the
  8113. // code that handles a NULL Buffer throughout these functions.
  8114. VOID
  8115. PfSvChunkAllocatorInitialize (
  8116. PPFSVC_CHUNK_ALLOCATOR Allocator
  8117. )
  8118. /*++
  8119. Routine Description:
  8120. Initializes the allocator structure. Must be called before other allocator
  8121. routines.
  8122. Arguments:
  8123. Allocator - Pointer to structure.
  8124. Return Value:
  8125. None.
  8126. --*/
  8127. {
  8128. //
  8129. // Zero the structure. This effectively initializes the following fields:
  8130. // Buffer
  8131. // BufferEnd
  8132. // FreePointer
  8133. // ChunkSize
  8134. // MaxHeapAllocs
  8135. // NumHeapAllocs
  8136. // UserSpecifiedBuffer
  8137. //
  8138. RtlZeroMemory(Allocator, sizeof(PFSVC_CHUNK_ALLOCATOR));
  8139. return;
  8140. }
  8141. DWORD
  8142. PfSvChunkAllocatorStart (
  8143. IN PPFSVC_CHUNK_ALLOCATOR Allocator,
  8144. OPTIONAL IN PVOID Buffer,
  8145. IN ULONG ChunkSize,
  8146. IN ULONG MaxChunks
  8147. )
  8148. /*++
  8149. Routine Description:
  8150. Must be called before calling alloc/free on an allocator that has
  8151. been initialized.
  8152. Arguments:
  8153. Allocator - Pointer to initialized structure.
  8154. Buffer - If specified, it is the buffer that will be divided up into
  8155. MaxChunks of ChunkSize and given away. Otherwise a buffer will be
  8156. allocated. If specified, the user has to free the buffer after the
  8157. chunk allocator has been cleaned up. It should be aligned right.
  8158. ChunkSize - In bytes how big each allocated chunk will be.
  8159. e.g. sizeof(PFSVC_PAGE_NODE) It should be greater than
  8160. sizeof(DWORD).
  8161. MaxChunks - Max number of allocs that will be made from the allocator.
  8162. Return Value:
  8163. Win32 error code.
  8164. --*/
  8165. {
  8166. ULONG AllocationSize;
  8167. DWORD ErrorCode;
  8168. //
  8169. // Initialize locals.
  8170. //
  8171. AllocationSize = ChunkSize * MaxChunks;
  8172. //
  8173. // We should be initialized and we should not get started twice.
  8174. //
  8175. PFSVC_ASSERT(Allocator->Buffer == NULL);
  8176. //
  8177. // Chunk size should not be too small.
  8178. //
  8179. if (ChunkSize < sizeof(DWORD) || !MaxChunks) {
  8180. ErrorCode = ERROR_INVALID_PARAMETER;
  8181. goto cleanup;
  8182. }
  8183. //
  8184. // Did the user specify the buffer to use?
  8185. //
  8186. if (Buffer) {
  8187. Allocator->Buffer = Buffer;
  8188. Allocator->UserSpecifiedBuffer = TRUE;
  8189. } else {
  8190. Allocator->Buffer = PFSVC_ALLOC(AllocationSize);
  8191. if (!Allocator->Buffer) {
  8192. ErrorCode = ERROR_NOT_ENOUGH_MEMORY;
  8193. goto cleanup;
  8194. }
  8195. Allocator->UserSpecifiedBuffer = FALSE;
  8196. }
  8197. Allocator->BufferEnd = (PCHAR) Buffer + (ULONG_PTR) AllocationSize;
  8198. Allocator->FreePointer = Buffer;
  8199. Allocator->ChunkSize = ChunkSize;
  8200. ErrorCode = ERROR_SUCCESS;
  8201. cleanup:
  8202. return ErrorCode;
  8203. }
  8204. PVOID
  8205. PfSvChunkAllocatorAllocate (
  8206. PPFSVC_CHUNK_ALLOCATOR Allocator
  8207. )
  8208. /*++
  8209. Routine Description:
  8210. Returns a ChunkSize chunk allocated from Allocator. ChunkSize was specified
  8211. when Allocator was started. If a chunk is return the caller should free it
  8212. to this Allocator before uninitializing the Allocator.
  8213. Arguments:
  8214. Allocator - Pointer to started allocator.
  8215. Return Value:
  8216. NULL or chunk allocated from Allocator.
  8217. --*/
  8218. {
  8219. PVOID ReturnChunk;
  8220. //
  8221. // We should not be trying to make allocations before we start the
  8222. // allocator.
  8223. //
  8224. PFSVC_ASSERT(Allocator->Buffer && Allocator->ChunkSize);
  8225. //
  8226. // If we can allocate from our preallocated buffer do so. Otherwise we
  8227. // have to hit the heap.
  8228. //
  8229. if (Allocator->FreePointer >= Allocator->BufferEnd) {
  8230. Allocator->MaxHeapAllocs++;
  8231. ReturnChunk = PFSVC_ALLOC(Allocator->ChunkSize);
  8232. if (ReturnChunk) {
  8233. Allocator->NumHeapAllocs++;
  8234. }
  8235. } else {
  8236. ReturnChunk = Allocator->FreePointer;
  8237. Allocator->FreePointer += (ULONG_PTR) Allocator->ChunkSize;
  8238. }
  8239. return ReturnChunk;
  8240. }
  8241. VOID
  8242. PfSvChunkAllocatorFree (
  8243. PPFSVC_CHUNK_ALLOCATOR Allocator,
  8244. PVOID Allocation
  8245. )
  8246. /*++
  8247. Routine Description:
  8248. Frees a chunk allocated from the allocator. This may not make it available
  8249. for use by further allocations from the allocator.
  8250. Arguments:
  8251. Allocator - Pointer to started allocator.
  8252. Allocation - Allocation to free.
  8253. Return Value:
  8254. None.
  8255. --*/
  8256. {
  8257. //
  8258. // Is this within the preallocated block?
  8259. //
  8260. if ((PUCHAR) Allocation >= Allocator->Buffer &&
  8261. (PUCHAR) Allocation < Allocator->BufferEnd) {
  8262. //
  8263. // Mark this chunk freed.
  8264. //
  8265. *(PULONG)Allocation = PFSVC_CHUNK_ALLOCATOR_FREED_MAGIC;
  8266. } else {
  8267. //
  8268. // This chunk was allocated from heap.
  8269. //
  8270. PFSVC_ASSERT(Allocator->NumHeapAllocs && Allocator->MaxHeapAllocs);
  8271. Allocator->NumHeapAllocs--;
  8272. PFSVC_FREE(Allocation);
  8273. }
  8274. return;
  8275. }
  8276. VOID
  8277. PfSvChunkAllocatorCleanup (
  8278. PPFSVC_CHUNK_ALLOCATOR Allocator
  8279. )
  8280. /*++
  8281. Routine Description:
  8282. Cleans up resources associated with the allocator. There should not be
  8283. any outstanding allocations from the allocator when this function is
  8284. called.
  8285. Arguments:
  8286. Allocator - Pointer to initialized allocator.
  8287. Return Value:
  8288. None.
  8289. --*/
  8290. {
  8291. PCHAR CurrentChunk;
  8292. ULONG Magic;
  8293. if (Allocator->Buffer) {
  8294. #ifdef PFSVC_DBG
  8295. //
  8296. // Make sure all real heap allocations have been freed.
  8297. //
  8298. PFSVC_ASSERT(Allocator->NumHeapAllocs == 0);
  8299. //
  8300. // Make sure all allocated chunks have been freed. Check
  8301. // ChunkSize first, if it's corrupted we'd loop forever.
  8302. //
  8303. PFSVC_ASSERT(Allocator->ChunkSize);
  8304. for (CurrentChunk = Allocator->Buffer;
  8305. CurrentChunk < Allocator->FreePointer;
  8306. CurrentChunk += (ULONG_PTR) Allocator->ChunkSize) {
  8307. Magic = *(PULONG)CurrentChunk;
  8308. PFSVC_ASSERT(Magic == PFSVC_CHUNK_ALLOCATOR_FREED_MAGIC);
  8309. }
  8310. #endif // PFSVC_DBG
  8311. //
  8312. // If the buffer was allocated by us (and not specified by
  8313. // the user), free it.
  8314. //
  8315. if (!Allocator->UserSpecifiedBuffer) {
  8316. PFSVC_FREE(Allocator->Buffer);
  8317. }
  8318. #ifdef PFSVC_DBG
  8319. //
  8320. // Setup the fields so if we try to make allocations after cleaning up
  8321. // an allocator we'll hit an assert.
  8322. //
  8323. Allocator->FreePointer = Allocator->Buffer;
  8324. Allocator->Buffer = NULL;
  8325. #endif // PFSVC_DBG
  8326. }
  8327. return;
  8328. }
  8329. //
  8330. // Routines used to allocate / free path strings efficiently.
  8331. //
  8332. VOID
  8333. PfSvStringAllocatorInitialize (
  8334. PPFSVC_STRING_ALLOCATOR Allocator
  8335. )
  8336. /*++
  8337. Routine Description:
  8338. Initializes the allocator structure. Must be called before other allocator
  8339. routines.
  8340. Arguments:
  8341. Allocator - Pointer to structure.
  8342. Return Value:
  8343. None.
  8344. --*/
  8345. {
  8346. //
  8347. // Zero the structure. This effectively initializes the following fields:
  8348. // Buffer
  8349. // BufferEnd
  8350. // FreePointer
  8351. // MaxHeapAllocs
  8352. // NumHeapAllocs
  8353. // LastAllocationSize
  8354. // UserSpecifiedBuffer
  8355. //
  8356. RtlZeroMemory(Allocator, sizeof(PFSVC_STRING_ALLOCATOR));
  8357. return;
  8358. }
  8359. DWORD
  8360. PfSvStringAllocatorStart (
  8361. IN PPFSVC_STRING_ALLOCATOR Allocator,
  8362. OPTIONAL IN PVOID Buffer,
  8363. IN ULONG MaxSize
  8364. )
  8365. /*++
  8366. Routine Description:
  8367. Must be called before calling alloc/free on an allocator that has
  8368. been initialized.
  8369. Arguments:
  8370. Allocator - Pointer to initialized structure.
  8371. Buffer - If specified, it is the buffer that we will allocate strings
  8372. from. Otherwise a buffer will be allocated. If specified, the user
  8373. has to free the buffer after the string allocator has been cleaned up.
  8374. It should be aligned right.
  8375. MaxSize - Max valid size of buffer in bytes.
  8376. Return Value:
  8377. Win32 error code.
  8378. --*/
  8379. {
  8380. DWORD ErrorCode;
  8381. //
  8382. // We should be initialized and we should not get started twice.
  8383. //
  8384. PFSVC_ASSERT(Allocator->Buffer == NULL);
  8385. //
  8386. // Did the user specify the buffer to use?
  8387. //
  8388. if (Buffer) {
  8389. Allocator->Buffer = Buffer;
  8390. Allocator->UserSpecifiedBuffer = TRUE;
  8391. } else {
  8392. Allocator->Buffer = PFSVC_ALLOC(MaxSize);
  8393. if (!Allocator->Buffer) {
  8394. ErrorCode = ERROR_NOT_ENOUGH_MEMORY;
  8395. goto cleanup;
  8396. }
  8397. Allocator->UserSpecifiedBuffer = FALSE;
  8398. }
  8399. Allocator->BufferEnd = (PCHAR) Buffer + (ULONG_PTR) MaxSize;
  8400. Allocator->FreePointer = Buffer;
  8401. ErrorCode = ERROR_SUCCESS;
  8402. cleanup:
  8403. return ErrorCode;
  8404. }
  8405. PVOID
  8406. PfSvStringAllocatorAllocate (
  8407. PPFSVC_STRING_ALLOCATOR Allocator,
  8408. ULONG NumBytes
  8409. )
  8410. /*++
  8411. Routine Description:
  8412. Returns a NumBytes size buffer allocated from Allocator.
  8413. This buffer should be freed back to the Allocator before the it is
  8414. uninitialized.
  8415. Arguments:
  8416. Allocator - Pointer to started allocator.
  8417. NumBytes - Number of bytes to allocate.
  8418. Return Value:
  8419. NULL or buffer allocated from Allocator.
  8420. --*/
  8421. {
  8422. PVOID ReturnChunk;
  8423. PPFSVC_STRING_ALLOCATION_HEADER AllocationHeader;
  8424. ULONG_PTR RealAllocationSize;
  8425. //
  8426. // We should not be trying to make allocations before we start the
  8427. // allocator.
  8428. //
  8429. PFSVC_ASSERT(Allocator->Buffer);
  8430. //
  8431. // Calculate how much we have to reserve from the buffer to make this
  8432. // allocation.
  8433. //
  8434. RealAllocationSize = 0;
  8435. RealAllocationSize += sizeof(PFSVC_STRING_ALLOCATION_HEADER);
  8436. RealAllocationSize += NumBytes;
  8437. RealAllocationSize = (ULONG_PTR) PF_ALIGN_UP(RealAllocationSize, _alignof(PFSVC_STRING_ALLOCATION_HEADER));
  8438. //
  8439. // We can't allocate from our buffer and have to go to the heap if
  8440. // - We've run out of space.
  8441. // - Allocation size is too big to fit in a USHORT.
  8442. // - It is a 0 sized allocation.
  8443. //
  8444. if (Allocator->FreePointer + RealAllocationSize > Allocator->BufferEnd ||
  8445. NumBytes > PFSVC_STRING_ALLOCATOR_MAX_BUFFER_ALLOCATION_SIZE ||
  8446. NumBytes == 0) {
  8447. //
  8448. // Hit the heap.
  8449. //
  8450. Allocator->MaxHeapAllocs++;
  8451. ReturnChunk = PFSVC_ALLOC(NumBytes);
  8452. if (ReturnChunk) {
  8453. Allocator->NumHeapAllocs++;
  8454. }
  8455. } else {
  8456. AllocationHeader = (PVOID) Allocator->FreePointer;
  8457. AllocationHeader->PrecedingAllocationSize = Allocator->LastAllocationSize;
  8458. PFSVC_ASSERT(RealAllocationSize < USHRT_MAX);
  8459. AllocationHeader->AllocationSize = (USHORT) RealAllocationSize;
  8460. Allocator->FreePointer += RealAllocationSize;
  8461. Allocator->LastAllocationSize = (USHORT) RealAllocationSize;
  8462. //
  8463. // The user's allocation comes right after the allocation header.
  8464. // (Using pointer arithmetic...)
  8465. //
  8466. ReturnChunk = AllocationHeader + 1;
  8467. }
  8468. return ReturnChunk;
  8469. }
  8470. VOID
  8471. PfSvStringAllocatorFree (
  8472. PPFSVC_STRING_ALLOCATOR Allocator,
  8473. PVOID Allocation
  8474. )
  8475. /*++
  8476. Routine Description:
  8477. Frees a string allocated from the allocator. This may not make it available
  8478. for use by further allocations from the allocator.
  8479. Arguments:
  8480. Allocator - Pointer to started allocator.
  8481. Allocation - Allocation to free.
  8482. Return Value:
  8483. None.
  8484. --*/
  8485. {
  8486. //
  8487. // Is this within the preallocated block?
  8488. //
  8489. if ((PUCHAR) Allocation >= Allocator->Buffer &&
  8490. (PUCHAR) Allocation < Allocator->BufferEnd) {
  8491. //
  8492. // Mark this chunk freed.
  8493. //
  8494. *((PWCHAR)Allocation) = PFSVC_STRING_ALLOCATOR_FREED_MAGIC;
  8495. } else {
  8496. //
  8497. // This chunk was allocated from heap.
  8498. //
  8499. PFSVC_ASSERT(Allocator->NumHeapAllocs && Allocator->MaxHeapAllocs);
  8500. Allocator->NumHeapAllocs--;
  8501. PFSVC_FREE(Allocation);
  8502. }
  8503. return;
  8504. }
  8505. VOID
  8506. PfSvStringAllocatorCleanup (
  8507. PPFSVC_STRING_ALLOCATOR Allocator
  8508. )
  8509. /*++
  8510. Routine Description:
  8511. Cleans up resources associated with the allocator. There should not be
  8512. any outstanding allocations from the allocator when this function is
  8513. called.
  8514. Arguments:
  8515. Allocator - Pointer to initialized allocator.
  8516. Return Value:
  8517. None.
  8518. --*/
  8519. {
  8520. PPFSVC_STRING_ALLOCATION_HEADER AllocationHeader;
  8521. PCHAR NextAllocationHeader;
  8522. WCHAR Magic;
  8523. if (Allocator->Buffer) {
  8524. #ifdef PFSVC_DBG
  8525. //
  8526. // Make sure all real heap allocations have been freed.
  8527. //
  8528. PFSVC_ASSERT(Allocator->NumHeapAllocs == 0);
  8529. //
  8530. // Make sure all allocated strings have been freed.
  8531. //
  8532. for (AllocationHeader = (PVOID) Allocator->Buffer;
  8533. (PCHAR) AllocationHeader < (PCHAR) Allocator->FreePointer;
  8534. AllocationHeader = (PVOID) NextAllocationHeader) {
  8535. Magic = *((PWCHAR)(AllocationHeader + 1));
  8536. PFSVC_ASSERT(Magic == PFSVC_STRING_ALLOCATOR_FREED_MAGIC);
  8537. //
  8538. // Calculate where the NextAllocationHeader will be.
  8539. //
  8540. NextAllocationHeader = (PCHAR) AllocationHeader +
  8541. (ULONG_PTR) AllocationHeader->AllocationSize;
  8542. }
  8543. #endif // PFSVC_DBG
  8544. //
  8545. // If the buffer was allocated by us (and not specified by
  8546. // the user), free it.
  8547. //
  8548. if (!Allocator->UserSpecifiedBuffer) {
  8549. PFSVC_FREE(Allocator->Buffer);
  8550. }
  8551. #ifdef PFSVC_DBG
  8552. //
  8553. // Setup the fields so if we try to make allocations after cleaning up
  8554. // an allocator we'll hit an assert.
  8555. //
  8556. Allocator->FreePointer = Allocator->Buffer;
  8557. Allocator->Buffer = NULL;
  8558. #endif // PFSVC_DBG
  8559. }
  8560. return;
  8561. }
  8562. //
  8563. // Routines that deal with section node structures.
  8564. //
  8565. VOID
  8566. PfSvCleanupSectionNode(
  8567. PPFSVC_SCENARIO_INFO ScenarioInfo,
  8568. PPFSVC_SECTION_NODE SectionNode
  8569. )
  8570. /*++
  8571. Routine Description:
  8572. This function cleans up a section node structure. It does not free
  8573. the structure itself.
  8574. Arguments:
  8575. ScenarioInfo - Pointer to scenario info.
  8576. SectionNode - Pointer to structure.
  8577. Return Value:
  8578. None.
  8579. --*/
  8580. {
  8581. PPFSVC_PAGE_NODE PageNode;
  8582. PLIST_ENTRY ListHead;
  8583. //
  8584. // If there is an allocated file name, free it.
  8585. //
  8586. if (SectionNode->FilePath) {
  8587. PfSvStringAllocatorFree(&ScenarioInfo->PathAllocator, SectionNode->FilePath);
  8588. SectionNode->FilePath = NULL;
  8589. }
  8590. //
  8591. // Free all the page nodes for this section.
  8592. //
  8593. while (!IsListEmpty(&SectionNode->PageList)) {
  8594. ListHead = RemoveHeadList(&SectionNode->PageList);
  8595. PageNode = CONTAINING_RECORD(ListHead, PFSVC_PAGE_NODE, PageLink);
  8596. PfSvChunkAllocatorFree(&ScenarioInfo->PageNodeAllocator, PageNode);
  8597. }
  8598. //
  8599. // We should not be on a volume node's list if we are being
  8600. // cleaned up.
  8601. //
  8602. PFSVC_ASSERT(IsListEmpty(&SectionNode->SectionVolumeLink));
  8603. }
  8604. //
  8605. // Routines used to sort scenario's section nodes.
  8606. //
  8607. DWORD
  8608. PfSvSortSectionNodesByFirstAccess(
  8609. PLIST_ENTRY SectionNodeList
  8610. )
  8611. /*++
  8612. Routine Description:
  8613. This routine will sort the specified section node list by first
  8614. access using NewSectionIndex and OrgSectionIndex of the section
  8615. nodes.
  8616. Arguments:
  8617. SectionNodeList - Pointer to list of section nodes to be sorted.
  8618. Return Value:
  8619. Win32 error code.
  8620. --*/
  8621. {
  8622. PFSV_SECTNODE_PRIORITY_QUEUE SortQueue;
  8623. PLIST_ENTRY SectHead;
  8624. PPFSVC_SECTION_NODE SectionNode;
  8625. DWORD ErrorCode;
  8626. //
  8627. // Initialize locals.
  8628. //
  8629. PfSvInitializeSectNodePriorityQueue(&SortQueue);
  8630. DBGPR((PFID,PFSTRC,"PFSVC: SortByFirstAccess(%p)\n", SectionNodeList));
  8631. //
  8632. // We have to sort the section nodes by first access. Remove
  8633. // section nodes from the scenario list and put them on a priority
  8634. // queue. [Bummer, it may have been a little faster if we had
  8635. // built a binary tree and traversed that in the rest of the code]
  8636. //
  8637. while (!IsListEmpty(SectionNodeList)) {
  8638. //
  8639. // The section list is sorted by name. It is more likely that
  8640. // we also accessed files by name. So to make the priority
  8641. // queue act better in such cases, remove from the tail of the
  8642. // list to insert into the priority queue.
  8643. //
  8644. SectHead = RemoveTailList(SectionNodeList);
  8645. SectionNode = CONTAINING_RECORD(SectHead,
  8646. PFSVC_SECTION_NODE,
  8647. SectionLink);
  8648. PfSvInsertSectNodePriorityQueue(&SortQueue, SectionNode);
  8649. }
  8650. //
  8651. // Remove the section nodes from the priority queue sorted by
  8652. // first access and put them to the tail of the section node list.
  8653. //
  8654. while (SectionNode = PfSvRemoveMinSectNodePriorityQueue(&SortQueue)) {
  8655. InsertTailList(SectionNodeList, &SectionNode->SectionLink);
  8656. }
  8657. ErrorCode = ERROR_SUCCESS;
  8658. DBGPR((PFID,PFSTRC,"PFSVC: SortByFirstAccess(%p)=%x\n", SectionNodeList, ErrorCode));
  8659. return ErrorCode;
  8660. }
  8661. PFSV_SECTION_NODE_COMPARISON_RESULT
  8662. FASTCALL
  8663. PfSvSectionNodeComparisonRoutine(
  8664. PPFSVC_SECTION_NODE Element1,
  8665. PPFSVC_SECTION_NODE Element2
  8666. )
  8667. /*++
  8668. Routine Description:
  8669. This routine is called to compare to elements when sorting the
  8670. section nodes array by first access.
  8671. Arguments:
  8672. Element1, Element2 - The two elements to compare.
  8673. Return Value:
  8674. PFSVC_SECTION_NODE_COMPARISON_RESULT
  8675. --*/
  8676. {
  8677. //
  8678. // First compare first-access index in the new trace.
  8679. //
  8680. if (Element1->NewSectionIndex < Element2->NewSectionIndex) {
  8681. return PfSvSectNode1LessThanSectNode2;
  8682. } else if (Element1->NewSectionIndex > Element2->NewSectionIndex) {
  8683. return PfSvSectNode1GreaterThanSectNode2;
  8684. } else {
  8685. //
  8686. // Next compare first-access index in the current scenario
  8687. // file.
  8688. //
  8689. if (Element1->OrgSectionIndex < Element2->OrgSectionIndex) {
  8690. return PfSvSectNode1LessThanSectNode2;
  8691. } else if (Element1->OrgSectionIndex > Element2->OrgSectionIndex) {
  8692. return PfSvSectNode1GreaterThanSectNode2;
  8693. } else {
  8694. return PfSvSectNode1EqualToSectNode2;
  8695. }
  8696. }
  8697. }
  8698. //
  8699. // Routines that implement a priority queue used to sort section nodes
  8700. // for a scenario.
  8701. //
  8702. VOID
  8703. PfSvInitializeSectNodePriorityQueue(
  8704. PPFSV_SECTNODE_PRIORITY_QUEUE PriorityQueue
  8705. )
  8706. /*++
  8707. Routine Description:
  8708. Initialize a section node priority queue.
  8709. Arguments:
  8710. PriorityQueue - Pointer to the queue.
  8711. Return Value:
  8712. None.
  8713. --*/
  8714. {
  8715. PriorityQueue->Head = NULL;
  8716. }
  8717. VOID
  8718. PfSvInsertSectNodePriorityQueue(
  8719. PPFSV_SECTNODE_PRIORITY_QUEUE PriorityQueue,
  8720. PPFSVC_SECTION_NODE NewElement
  8721. )
  8722. /*++
  8723. Routine Description:
  8724. Insert a section node in the a section node priority queue.
  8725. Arguments:
  8726. PriorityQueue - Pointer to the queue.
  8727. NewElement - Pointer to new element.
  8728. Return Value:
  8729. None.
  8730. --*/
  8731. {
  8732. PPFSVC_SECTION_NODE *CurrentPosition;
  8733. //
  8734. // Initialize the link fields of NewElement.
  8735. //
  8736. NewElement->LeftChild = NULL;
  8737. NewElement->RightChild = NULL;
  8738. //
  8739. // If the queue is empty, insert this at the head.
  8740. //
  8741. if (PriorityQueue->Head == NULL) {
  8742. PriorityQueue->Head = NewElement;
  8743. return;
  8744. }
  8745. //
  8746. // If we are less than the current min element, put us at the
  8747. // head.
  8748. //
  8749. if (PfSvSectionNodeComparisonRoutine(NewElement, PriorityQueue->Head) <= 0) {
  8750. NewElement->RightChild = PriorityQueue->Head;
  8751. PriorityQueue->Head = NewElement;
  8752. return;
  8753. }
  8754. //
  8755. // Insert this node into the tree rooted at the right child of the
  8756. // head node.
  8757. //
  8758. CurrentPosition = &PriorityQueue->Head->RightChild;
  8759. while (*CurrentPosition) {
  8760. if (PfSvSectionNodeComparisonRoutine(NewElement, *CurrentPosition) <= 0) {
  8761. CurrentPosition = &(*CurrentPosition)->LeftChild;
  8762. } else {
  8763. CurrentPosition = &(*CurrentPosition)->RightChild;
  8764. }
  8765. }
  8766. //
  8767. // We found the place.
  8768. //
  8769. *CurrentPosition = NewElement;
  8770. }
  8771. PPFSVC_SECTION_NODE
  8772. PfSvRemoveMinSectNodePriorityQueue(
  8773. PPFSV_SECTNODE_PRIORITY_QUEUE PriorityQueue
  8774. )
  8775. /*++
  8776. Routine Description:
  8777. Remove the head element of the queue.
  8778. Arguments:
  8779. PriorityQueue - Pointer to the queue.
  8780. Return Value:
  8781. Pointer to head element of the queue.
  8782. NULL if queue is empty.
  8783. --*/
  8784. {
  8785. PPFSVC_SECTION_NODE *CurrentPosition;
  8786. PPFSVC_SECTION_NODE OrgHeadNode;
  8787. PPFSVC_SECTION_NODE NewHeadNode;
  8788. PPFSVC_SECTION_NODE TreeRoot;
  8789. //
  8790. // If the queue is empty return NULL.
  8791. //
  8792. if (PriorityQueue->Head == NULL) {
  8793. return NULL;
  8794. }
  8795. //
  8796. // Save pointer to original head node.
  8797. //
  8798. OrgHeadNode = PriorityQueue->Head;
  8799. //
  8800. // Find the minimum element of the tree rooted at the right child
  8801. // of the head node. CurrentPosition points to the link of the
  8802. // parent to the smaller child.
  8803. //
  8804. TreeRoot = OrgHeadNode->RightChild;
  8805. CurrentPosition = &TreeRoot;
  8806. while (*CurrentPosition && (*CurrentPosition)->LeftChild) {
  8807. CurrentPosition = &(*CurrentPosition)->LeftChild;
  8808. }
  8809. NewHeadNode = *CurrentPosition;
  8810. //
  8811. // Check if there is really a new head node that we have to remove
  8812. // from its current position.
  8813. //
  8814. if (NewHeadNode) {
  8815. //
  8816. // We are removing this node to put it at the head. In its
  8817. // place, we'll put its right child. Since we know that this
  8818. // node does not have a left child, that's all we have to do.
  8819. //
  8820. *CurrentPosition = NewHeadNode->RightChild;
  8821. //
  8822. // Set the tree rooted at the head's right child.
  8823. //
  8824. NewHeadNode->RightChild = TreeRoot;
  8825. }
  8826. //
  8827. // Set the new head.
  8828. //
  8829. PriorityQueue->Head = NewHeadNode;
  8830. //
  8831. // Return the original head node.
  8832. //
  8833. return OrgHeadNode;
  8834. }
  8835. //
  8836. // Implementation of the Nt path to Dos path translation API.
  8837. //
  8838. DWORD
  8839. PfSvBuildNtPathTranslationList(
  8840. PNTPATH_TRANSLATION_LIST *NtPathTranslationList
  8841. )
  8842. /*++
  8843. Routine Description:
  8844. This routine is called to build a list that can be used to
  8845. translate Nt paths to Dos paths. If successful, the returned list
  8846. should be freed by calling PfSvFreeNtPathTranslationList.
  8847. Arguments:
  8848. TranslationList - Pointer to where a pointer to the built
  8849. translation list is going to be put.
  8850. Return Value:
  8851. Win32 error code.
  8852. --*/
  8853. {
  8854. DWORD ErrorCode;
  8855. ULONG VolumeNameLength;
  8856. ULONG VolumeNameMaxLength;
  8857. PWCHAR VolumeName;
  8858. ULONG NTDevicePathMaxLength;
  8859. ULONG NTDevicePathLength;
  8860. PWCHAR NTDevicePath;
  8861. HANDLE FindVolumeHandle;
  8862. ULONG RequiredLength;
  8863. ULONG VolumePathNamesLength;
  8864. WCHAR *VolumePathNames;
  8865. ULONG MountPathNameLength;
  8866. WCHAR *MountPathName;
  8867. ULONG ShortestMountPathLength;
  8868. WCHAR *ShortestMountPathName;
  8869. ULONG NumMountPoints;
  8870. ULONG NumResizes;
  8871. BOOL Result;
  8872. ULONG NumChars;
  8873. ULONG Length;
  8874. PNTPATH_TRANSLATION_LIST TranslationList;
  8875. PNTPATH_TRANSLATION_ENTRY TranslationEntry;
  8876. PNTPATH_TRANSLATION_ENTRY NextTranslationEntry;
  8877. ULONG AllocationSize;
  8878. PUCHAR DestinationPointer;
  8879. ULONG CopySize;
  8880. PLIST_ENTRY HeadEntry;
  8881. PLIST_ENTRY NextEntry;
  8882. PLIST_ENTRY InsertPosition;
  8883. BOOLEAN TrimmedTerminatingSlash;
  8884. //
  8885. // Initialize locals.
  8886. //
  8887. FindVolumeHandle = INVALID_HANDLE_VALUE;
  8888. VolumePathNames = NULL;
  8889. VolumePathNamesLength = 0;
  8890. VolumeName = NULL;
  8891. VolumeNameMaxLength = 0;
  8892. NTDevicePath = NULL;
  8893. NTDevicePathMaxLength = 0;
  8894. TranslationList = NULL;
  8895. DBGPR((PFID,PFTRC,"PFSVC: BuildTransList()\n"));
  8896. //
  8897. // Allocate intermediate buffers.
  8898. //
  8899. Length = MAX_PATH + 1;
  8900. VolumeName = PFSVC_ALLOC(Length * sizeof(WCHAR));
  8901. NTDevicePath = PFSVC_ALLOC(Length * sizeof(WCHAR));
  8902. if (!VolumeName || !NTDevicePath) {
  8903. ErrorCode = ERROR_NOT_ENOUGH_MEMORY;
  8904. goto cleanup;
  8905. }
  8906. VolumeNameMaxLength = Length;
  8907. NTDevicePathMaxLength = Length;
  8908. //
  8909. // Allocate and initialize a translation list.
  8910. //
  8911. TranslationList = PFSVC_ALLOC(sizeof(NTPATH_TRANSLATION_LIST));
  8912. if (!TranslationList) {
  8913. ErrorCode = ERROR_NOT_ENOUGH_MEMORY;
  8914. goto cleanup;
  8915. }
  8916. InitializeListHead(TranslationList);
  8917. //
  8918. // Start enumerating the volumes.
  8919. //
  8920. FindVolumeHandle = FindFirstVolume(VolumeName, VolumeNameMaxLength);
  8921. if (FindVolumeHandle == INVALID_HANDLE_VALUE) {
  8922. ErrorCode = GetLastError();
  8923. goto cleanup;
  8924. }
  8925. do {
  8926. //
  8927. // Update volume length after the FindFirst/NextVolume call.
  8928. //
  8929. VolumeNameLength = wcslen(VolumeName);
  8930. //
  8931. // Get list of where this volume is mounted.
  8932. //
  8933. NumResizes = 0;
  8934. do {
  8935. Result = GetVolumePathNamesForVolumeName(VolumeName,
  8936. VolumePathNames,
  8937. VolumePathNamesLength,
  8938. &RequiredLength);
  8939. if (Result) {
  8940. //
  8941. // We got the mount points.
  8942. //
  8943. break;
  8944. }
  8945. //
  8946. // Check why we failed.
  8947. //
  8948. ErrorCode = GetLastError();
  8949. if (ErrorCode != ERROR_MORE_DATA) {
  8950. //
  8951. // A real error...
  8952. //
  8953. goto cleanup;
  8954. }
  8955. //
  8956. // We need to increase the size of our buffer. If there is
  8957. // an existing buffer, first free it.
  8958. //
  8959. if (VolumePathNames) {
  8960. PFSVC_FREE(VolumePathNames);
  8961. VolumePathNames = NULL;
  8962. VolumePathNamesLength = 0;
  8963. }
  8964. //
  8965. // Try to allocate a new buffer.
  8966. //
  8967. VolumePathNames = PFSVC_ALLOC(RequiredLength * sizeof(WCHAR));
  8968. if (!VolumePathNames) {
  8969. ErrorCode = ERROR_NOT_ENOUGH_MEMORY;
  8970. goto cleanup;
  8971. }
  8972. VolumePathNamesLength = RequiredLength;
  8973. //
  8974. // Retry with the resized buffer but make sure we don't
  8975. // loop forever!
  8976. //
  8977. NumResizes++;
  8978. if (NumResizes > 1000) {
  8979. ErrorCode = ERROR_INVALID_FUNCTION;
  8980. goto cleanup;
  8981. }
  8982. } while (TRUE);
  8983. //
  8984. // Loop through the mount points to find the shortest one. It
  8985. // is possible that the depth of it is more.
  8986. //
  8987. MountPathName = VolumePathNames;
  8988. NumMountPoints = 0;
  8989. ShortestMountPathName = NULL;
  8990. ShortestMountPathLength = ULONG_MAX;
  8991. while (*MountPathName) {
  8992. MountPathNameLength = wcslen(MountPathName);
  8993. if (MountPathNameLength < ShortestMountPathLength) {
  8994. ShortestMountPathName = MountPathName;
  8995. ShortestMountPathLength = MountPathNameLength;
  8996. }
  8997. NumMountPoints++;
  8998. //
  8999. // Update the pointer to next mount point path.
  9000. //
  9001. MountPathName += MountPathNameLength + 1;
  9002. }
  9003. //
  9004. // Check if we got a mount point path.
  9005. //
  9006. if (ShortestMountPathName == NULL) {
  9007. //
  9008. // Skip this volume.
  9009. //
  9010. continue;
  9011. }
  9012. //
  9013. // Remove the terminating slash if there is one.
  9014. //
  9015. if (ShortestMountPathName[ShortestMountPathLength - 1] == L'\\') {
  9016. ShortestMountPathName[ShortestMountPathLength - 1] = 0;
  9017. ShortestMountPathLength--;
  9018. }
  9019. //
  9020. // Get NT device that is the target of the volume link in
  9021. // Win32 object namespace. We get the dos device name by
  9022. // trimming the first 4 characters [i.e. \\?\] of the
  9023. // VolumeName. Also trim the \ at the very end of the volume
  9024. // name.
  9025. //
  9026. if (VolumeNameLength <= 4) {
  9027. ErrorCode = ERROR_BAD_FORMAT;
  9028. goto cleanup;
  9029. }
  9030. if (VolumeName[VolumeNameLength - 1] == L'\\') {
  9031. VolumeName[VolumeNameLength - 1] = 0;
  9032. TrimmedTerminatingSlash = TRUE;
  9033. } else {
  9034. TrimmedTerminatingSlash = FALSE;
  9035. }
  9036. NumChars = QueryDosDevice(&VolumeName[4],
  9037. NTDevicePath,
  9038. NTDevicePathMaxLength);
  9039. if (TrimmedTerminatingSlash) {
  9040. VolumeName[VolumeNameLength - 1] = L'\\';
  9041. }
  9042. if (NumChars == 0) {
  9043. ErrorCode = GetLastError();
  9044. goto cleanup;
  9045. }
  9046. //
  9047. // We are interested only in the current mapping.
  9048. //
  9049. NTDevicePath[NTDevicePathMaxLength - 1] = 0;
  9050. NTDevicePathLength = wcslen(NTDevicePath);
  9051. if (NTDevicePathLength == 0) {
  9052. //
  9053. // Skip this volume.
  9054. //
  9055. continue;
  9056. }
  9057. //
  9058. // Remove terminating slash if there is one.
  9059. //
  9060. if (NTDevicePath[NTDevicePathLength - 1] == L'\\') {
  9061. NTDevicePath[NTDevicePathLength - 1] = 0;
  9062. NTDevicePathLength--;
  9063. }
  9064. //
  9065. // Allocate a translation entry big enough to contain both
  9066. // path names and the volume string.
  9067. //
  9068. AllocationSize = sizeof(NTPATH_TRANSLATION_ENTRY);
  9069. AllocationSize += (ShortestMountPathLength + 1) * sizeof(WCHAR);
  9070. AllocationSize += (NTDevicePathLength + 1) * sizeof(WCHAR);
  9071. AllocationSize += (VolumeNameLength + 1) * sizeof(WCHAR);
  9072. TranslationEntry = PFSVC_ALLOC(AllocationSize);
  9073. if (!TranslationEntry) {
  9074. ErrorCode = ERROR_NOT_ENOUGH_MEMORY;
  9075. goto cleanup;
  9076. }
  9077. DestinationPointer = (PUCHAR) TranslationEntry;
  9078. DestinationPointer += sizeof(NTPATH_TRANSLATION_ENTRY);
  9079. //
  9080. // Copy the NT path name and the terminating NUL.
  9081. //
  9082. TranslationEntry->NtPrefix = (PVOID) DestinationPointer;
  9083. TranslationEntry->NtPrefixLength = NTDevicePathLength;
  9084. CopySize = (NTDevicePathLength + 1) * sizeof(WCHAR);
  9085. RtlCopyMemory(DestinationPointer, NTDevicePath, CopySize);
  9086. DestinationPointer += CopySize;
  9087. //
  9088. // Copy the DOS mount point name and the terminating NUL.
  9089. //
  9090. TranslationEntry->DosPrefix = (PVOID) DestinationPointer;
  9091. TranslationEntry->DosPrefixLength = ShortestMountPathLength;
  9092. CopySize = (ShortestMountPathLength + 1) * sizeof(WCHAR);
  9093. RtlCopyMemory(DestinationPointer, ShortestMountPathName, CopySize);
  9094. DestinationPointer += CopySize;
  9095. //
  9096. // Copy the volume name and the terminating NUL.
  9097. //
  9098. TranslationEntry->VolumeName = (PVOID) DestinationPointer;
  9099. TranslationEntry->VolumeNameLength = VolumeNameLength;
  9100. CopySize = (VolumeNameLength + 1) * sizeof(WCHAR);
  9101. RtlCopyMemory(DestinationPointer, VolumeName, CopySize);
  9102. DestinationPointer += CopySize;
  9103. //
  9104. // Find the position for this entry in the sorted translation
  9105. // list.
  9106. //
  9107. HeadEntry = TranslationList;
  9108. NextEntry = HeadEntry->Flink;
  9109. InsertPosition = HeadEntry;
  9110. while (NextEntry != HeadEntry) {
  9111. NextTranslationEntry = CONTAINING_RECORD(NextEntry,
  9112. NTPATH_TRANSLATION_ENTRY,
  9113. Link);
  9114. if (_wcsicmp(TranslationEntry->NtPrefix,
  9115. NextTranslationEntry->NtPrefix) <= 0) {
  9116. break;
  9117. }
  9118. InsertPosition = NextEntry;
  9119. NextEntry = NextEntry->Flink;
  9120. }
  9121. //
  9122. // Insert it after the found position.
  9123. //
  9124. InsertHeadList(InsertPosition, &TranslationEntry->Link);
  9125. } while (FindNextVolume(FindVolumeHandle, VolumeName, VolumeNameMaxLength));
  9126. //
  9127. // We will break out of the loop when FindNextVolume does not
  9128. // return success. Check if it failed for a reason other than that
  9129. // we have enumerated all volumes.
  9130. //
  9131. ErrorCode = GetLastError();
  9132. if (ErrorCode != ERROR_NO_MORE_FILES) {
  9133. goto cleanup;
  9134. }
  9135. //
  9136. // Set return value.
  9137. //
  9138. *NtPathTranslationList = TranslationList;
  9139. ErrorCode = ERROR_SUCCESS;
  9140. cleanup:
  9141. if (FindVolumeHandle != INVALID_HANDLE_VALUE) {
  9142. FindVolumeClose(FindVolumeHandle);
  9143. }
  9144. if (VolumePathNames) {
  9145. PFSVC_FREE(VolumePathNames);
  9146. }
  9147. if (ErrorCode != ERROR_SUCCESS) {
  9148. if (TranslationList) {
  9149. PfSvFreeNtPathTranslationList(TranslationList);
  9150. }
  9151. }
  9152. if (VolumeName) {
  9153. PFSVC_FREE(VolumeName);
  9154. }
  9155. if (NTDevicePath) {
  9156. PFSVC_FREE(NTDevicePath);
  9157. }
  9158. DBGPR((PFID,PFTRC,"PFSVC: BuildTransList()=%x,%p\n", ErrorCode, TranslationList));
  9159. return ErrorCode;
  9160. }
  9161. VOID
  9162. PfSvFreeNtPathTranslationList(
  9163. PNTPATH_TRANSLATION_LIST TranslationList
  9164. )
  9165. /*++
  9166. Routine Description:
  9167. This routine is called to free a translation list returned by
  9168. PfSvBuildNtPathTranslationList.
  9169. Arguments:
  9170. TranslationList - Pointer to list to free.
  9171. Return Value:
  9172. None.
  9173. --*/
  9174. {
  9175. PLIST_ENTRY HeadEntry;
  9176. PNTPATH_TRANSLATION_ENTRY TranslationEntry;
  9177. DBGPR((PFID,PFTRC,"PFSVC: FreeTransList(%p)\n", TranslationList));
  9178. //
  9179. // Free all entries in the list.
  9180. //
  9181. while (!IsListEmpty(TranslationList)) {
  9182. HeadEntry = RemoveHeadList(TranslationList);
  9183. TranslationEntry = CONTAINING_RECORD(HeadEntry,
  9184. NTPATH_TRANSLATION_ENTRY,
  9185. Link);
  9186. PFSVC_FREE(TranslationEntry);
  9187. }
  9188. //
  9189. // Free the list itself.
  9190. //
  9191. PFSVC_FREE(TranslationList);
  9192. }
  9193. DWORD
  9194. PfSvTranslateNtPath(
  9195. PNTPATH_TRANSLATION_LIST TranslationList,
  9196. WCHAR *NtPath,
  9197. ULONG NtPathLength,
  9198. PWCHAR *DosPathBuffer,
  9199. PULONG DosPathBufferSize
  9200. )
  9201. /*++
  9202. Routine Description:
  9203. This routine is called to free a translation list returned by
  9204. PfSvBuildNtPathTranslationList. Note that it may not be possible to
  9205. translate all Nt path's to a Dos path.
  9206. Arguments:
  9207. TranslationList - Pointer to list built by PfSvBuildNtPathTranslationList.
  9208. NtPath - Path to translate.
  9209. NtPathLength - Length of NtPath in characters excluding terminating NUL.
  9210. DosPathBuffer - Buffer to put the translation into. If it is NULL
  9211. or not big enough it will get reallocated. If a buffer is passed
  9212. in, it should be allocated by PFSVC_ALLOC. It is the callers
  9213. responsibility to free the buffer with PFSVC_FREE when done.
  9214. DosPathBufferSize - Size of DosPathBuffer in bytes. Updated if the
  9215. buffer is reallocated.
  9216. Return Value:
  9217. Win32 error code.
  9218. --*/
  9219. {
  9220. DWORD ErrorCode;
  9221. PLIST_ENTRY HeadEntry;
  9222. PLIST_ENTRY NextEntry;
  9223. PNTPATH_TRANSLATION_ENTRY CurrentTranslationEntry;
  9224. PNTPATH_TRANSLATION_ENTRY FoundTranslationEntry;
  9225. PFSV_PREFIX_COMPARISON_RESULT ComparisonResult;
  9226. ULONG RequiredSize;
  9227. //
  9228. // Initialize locals.
  9229. //
  9230. FoundTranslationEntry = NULL;
  9231. DBGPR((PFID,PFPATH,"PFSVC: TranslateNtPath(%ws)\n", NtPath));
  9232. //
  9233. // Walk through the sorted translation list to find an entry that
  9234. // applies.
  9235. //
  9236. HeadEntry = TranslationList;
  9237. NextEntry = HeadEntry->Flink;
  9238. while (NextEntry != HeadEntry) {
  9239. CurrentTranslationEntry = CONTAINING_RECORD(NextEntry,
  9240. NTPATH_TRANSLATION_ENTRY,
  9241. Link);
  9242. //
  9243. // Do a case insensitive comparison.
  9244. //
  9245. ComparisonResult = PfSvComparePrefix(NtPath,
  9246. NtPathLength,
  9247. CurrentTranslationEntry->NtPrefix,
  9248. CurrentTranslationEntry->NtPrefixLength,
  9249. FALSE);
  9250. if (ComparisonResult == PfSvPrefixIdentical) {
  9251. //
  9252. // Check to see if the character in NtPath after the
  9253. // prefix is a path seperator [i.e. '\']. Otherwise we may
  9254. // match \Device\CdRom10\DirName\FileName to \Device\Cdrom1.
  9255. //
  9256. if (NtPathLength == CurrentTranslationEntry->NtPrefixLength ||
  9257. NtPath[CurrentTranslationEntry->NtPrefixLength] == L'\\') {
  9258. //
  9259. // We found a translation entry that applies to us.
  9260. //
  9261. FoundTranslationEntry = CurrentTranslationEntry;
  9262. break;
  9263. }
  9264. } else if (ComparisonResult == PfSvPrefixGreaterThan) {
  9265. //
  9266. // Since the translation list is sorted in increasing
  9267. // order, following entries will also be greater than
  9268. // NtPath.
  9269. //
  9270. FoundTranslationEntry = NULL;
  9271. break;
  9272. }
  9273. //
  9274. // Continue looking for a matching prefix.
  9275. //
  9276. NextEntry = NextEntry->Flink;
  9277. }
  9278. //
  9279. // If we could not find an entry that applies we cannot translate
  9280. // the path.
  9281. //
  9282. if (FoundTranslationEntry == NULL) {
  9283. ErrorCode = ERROR_PATH_NOT_FOUND;
  9284. goto cleanup;
  9285. }
  9286. //
  9287. // Calculate required size: We will replace the NtPrefix with
  9288. // DosPrefix. Don't forget the terminating NUL character.
  9289. //
  9290. RequiredSize = (NtPathLength + 1) * sizeof(WCHAR);
  9291. RequiredSize += (FoundTranslationEntry->DosPrefixLength * sizeof(WCHAR));
  9292. RequiredSize -= (FoundTranslationEntry->NtPrefixLength * sizeof(WCHAR));
  9293. if (RequiredSize > (*DosPathBufferSize)) {
  9294. //
  9295. // Reallocate the buffer. First free it if there is one.
  9296. //
  9297. if (*DosPathBufferSize) {
  9298. PFSVC_ASSERT(*DosPathBuffer);
  9299. PFSVC_FREE(*DosPathBuffer);
  9300. (*DosPathBuffer) = NULL;
  9301. (*DosPathBufferSize) = 0;
  9302. }
  9303. PFSVC_ASSERT((*DosPathBuffer) == NULL);
  9304. (*DosPathBuffer) = PFSVC_ALLOC(RequiredSize);
  9305. if (!(*DosPathBuffer)) {
  9306. ErrorCode = ERROR_NOT_ENOUGH_MEMORY;
  9307. goto cleanup;
  9308. }
  9309. (*DosPathBufferSize) = RequiredSize;
  9310. }
  9311. //
  9312. // We should have enough room now.
  9313. //
  9314. PFSVC_ASSERT(RequiredSize <= (*DosPathBufferSize));
  9315. //
  9316. // Copy the DosPrefix.
  9317. //
  9318. wcscpy((*DosPathBuffer), FoundTranslationEntry->DosPrefix);
  9319. //
  9320. // Concatenate the remaining path.
  9321. //
  9322. wcscat((*DosPathBuffer), NtPath + CurrentTranslationEntry->NtPrefixLength);
  9323. //
  9324. // We are done.
  9325. //
  9326. ErrorCode = ERROR_SUCCESS;
  9327. cleanup:
  9328. DBGPR((PFID,PFPATH,"PFSVC: TranslateNtPath(%ws)=%x,%ws\n",
  9329. NtPath,ErrorCode,(ErrorCode==ERROR_SUCCESS)?(*DosPathBuffer):L"Failed"));
  9330. return ErrorCode;
  9331. }
  9332. //
  9333. // Implementation of the path list API.
  9334. //
  9335. VOID
  9336. PfSvInitializePathList(
  9337. PPFSVC_PATH_LIST PathList,
  9338. OPTIONAL IN PPFSVC_STRING_ALLOCATOR PathAllocator,
  9339. IN BOOLEAN CaseSensitive
  9340. )
  9341. /*++
  9342. Routine Description:
  9343. This function initializes a path list structure.
  9344. Arguments:
  9345. PathList - Pointer to structure.
  9346. PathAllocator - If specified path allocations will be made from it.
  9347. CaseSenstive - Whether list will be case senstive.
  9348. Return Value:
  9349. None.
  9350. --*/
  9351. {
  9352. InitializeListHead(&PathList->InOrderList);
  9353. InitializeListHead(&PathList->SortedList);
  9354. PathList->NumPaths = 0;
  9355. PathList->TotalLength = 0;
  9356. PathList->Allocator = PathAllocator;
  9357. PathList->CaseSensitive = CaseSensitive;
  9358. }
  9359. VOID
  9360. PfSvCleanupPathList(
  9361. PPFSVC_PATH_LIST PathList
  9362. )
  9363. /*++
  9364. Routine Description:
  9365. This function cleans up a path list structure. It does not free
  9366. the structure itself. The structure should have been initialized
  9367. by PfSvInitializePathList.
  9368. Arguments:
  9369. PathList - Pointer to structure.
  9370. Return Value:
  9371. None.
  9372. --*/
  9373. {
  9374. PLIST_ENTRY ListEntry;
  9375. PPFSVC_PATH Path;
  9376. while (!IsListEmpty(&PathList->InOrderList)) {
  9377. PFSVC_ASSERT(PathList->NumPaths);
  9378. PathList->NumPaths--;
  9379. ListEntry = RemoveHeadList(&PathList->InOrderList);
  9380. Path = CONTAINING_RECORD(ListEntry,
  9381. PFSVC_PATH,
  9382. InOrderLink);
  9383. if (PathList->Allocator) {
  9384. PfSvStringAllocatorFree(PathList->Allocator, Path);
  9385. } else {
  9386. PFSVC_FREE(Path);
  9387. }
  9388. }
  9389. }
  9390. BOOLEAN
  9391. PfSvIsInPathList(
  9392. PPFSVC_PATH_LIST PathList,
  9393. WCHAR *Path,
  9394. ULONG PathLength
  9395. )
  9396. /*++
  9397. Routine Description:
  9398. This function checks if the specified path is already in the path
  9399. list.
  9400. Arguments:
  9401. PathList - Pointer to list.
  9402. Path - Path to look for. Does not have to be NUL terminated.
  9403. PathLength - Length of Path in characters excluding NUL if there
  9404. is one.
  9405. Return Value:
  9406. Win32 error code.
  9407. --*/
  9408. {
  9409. PLIST_ENTRY HeadEntry;
  9410. PLIST_ENTRY NextEntry;
  9411. PPFSVC_PATH PathEntry;
  9412. INT ComparisonResult;
  9413. BOOLEAN PathIsInPathList;
  9414. //
  9415. // Walk through the list.
  9416. //
  9417. HeadEntry = &PathList->SortedList;
  9418. NextEntry = HeadEntry->Flink;
  9419. while (NextEntry != HeadEntry) {
  9420. PathEntry = CONTAINING_RECORD(NextEntry,
  9421. PFSVC_PATH,
  9422. SortedLink);
  9423. if (PathList->CaseSensitive) {
  9424. ComparisonResult = wcsncmp(Path,
  9425. PathEntry->Path,
  9426. PathLength);
  9427. } else {
  9428. ComparisonResult = _wcsnicmp(Path,
  9429. PathEntry->Path,
  9430. PathLength);
  9431. }
  9432. //
  9433. // Adjust comparison result so we don't match "abcde" to
  9434. // "abcdefg". If string comparison says the first PathLength
  9435. // characters match, check to see if PathEntry's length is
  9436. // longer, which would make it "greater" than the new path.
  9437. //
  9438. if (ComparisonResult == 0 && PathEntry->Length != PathLength) {
  9439. //
  9440. // The string comparison would not say The path entry's
  9441. // path is equal to path if its length was smaller.
  9442. //
  9443. PFSVC_ASSERT(PathEntry->Length > PathLength);
  9444. //
  9445. // Path is actually less than this path entry.
  9446. //
  9447. ComparisonResult = -1;
  9448. }
  9449. //
  9450. // Based on comparison result determine what to do:
  9451. //
  9452. if (ComparisonResult == 0) {
  9453. //
  9454. // We found it.
  9455. //
  9456. PathIsInPathList = TRUE;
  9457. goto cleanup;
  9458. } else if (ComparisonResult < 0) {
  9459. //
  9460. // We will be less than the rest of the strings in the
  9461. // list after this too.
  9462. //
  9463. PathIsInPathList = FALSE;
  9464. goto cleanup;
  9465. }
  9466. //
  9467. // Continue looking for the path or an available position.
  9468. //
  9469. NextEntry = NextEntry->Flink;
  9470. }
  9471. //
  9472. // If we came here, we could not find the path in the list.
  9473. //
  9474. PathIsInPathList = FALSE;
  9475. cleanup:
  9476. return PathIsInPathList;
  9477. }
  9478. DWORD
  9479. PfSvAddToPathList(
  9480. PPFSVC_PATH_LIST PathList,
  9481. WCHAR *Path,
  9482. ULONG PathLength
  9483. )
  9484. /*++
  9485. Routine Description:
  9486. This function adds a path to a path list. If the path already
  9487. exists in the list, it is not added again.
  9488. Arguments:
  9489. PathList - Pointer to list.
  9490. Path - Path to add. Does not need to be NUL terminated.
  9491. PathLength - Length of Path in characters excluding NUL if there
  9492. is one.
  9493. Return Value:
  9494. Win32 error code.
  9495. --*/
  9496. {
  9497. DWORD ErrorCode;
  9498. PLIST_ENTRY HeadEntry;
  9499. PLIST_ENTRY NextEntry;
  9500. PPFSVC_PATH PathEntry;
  9501. PPFSVC_PATH NewPathEntry;
  9502. INT ComparisonResult;
  9503. ULONG AllocationSize;
  9504. //
  9505. // Initialize locals.
  9506. //
  9507. NewPathEntry = NULL;
  9508. //
  9509. // Walk through the list to check if path is already in the list,
  9510. // or to find where it should be so we can insert it there.
  9511. //
  9512. HeadEntry = &PathList->SortedList;
  9513. NextEntry = HeadEntry->Flink;
  9514. while (NextEntry != HeadEntry) {
  9515. PathEntry = CONTAINING_RECORD(NextEntry,
  9516. PFSVC_PATH,
  9517. SortedLink);
  9518. if (PathList->CaseSensitive) {
  9519. ComparisonResult = wcsncmp(Path,
  9520. PathEntry->Path,
  9521. PathLength);
  9522. } else {
  9523. ComparisonResult = _wcsnicmp(Path,
  9524. PathEntry->Path,
  9525. PathLength);
  9526. }
  9527. //
  9528. // Adjust comparison result so we don't match "abcde" to
  9529. // "abcdefg". If string comparison says the first PathLength
  9530. // characters match, check to see if PathEntry's length is
  9531. // longer, which would make it "greater" than the new path.
  9532. //
  9533. if (ComparisonResult == 0 && PathEntry->Length != PathLength) {
  9534. //
  9535. // The string comparison would not say The path entry's
  9536. // path is equal to path if its length was smaller.
  9537. //
  9538. PFSVC_ASSERT(PathEntry->Length > PathLength);
  9539. //
  9540. // Path is actually less than this path entry.
  9541. //
  9542. ComparisonResult = -1;
  9543. }
  9544. //
  9545. // Based on comparison result determine what to do:
  9546. //
  9547. if (ComparisonResult == 0) {
  9548. //
  9549. // The path already exists in the list.
  9550. //
  9551. ErrorCode = ERROR_SUCCESS;
  9552. goto cleanup;
  9553. } else if (ComparisonResult < 0) {
  9554. //
  9555. // We will be less than the rest of the strings in the
  9556. // list after this too. We should be inserted before this
  9557. // one.
  9558. //
  9559. break;
  9560. }
  9561. //
  9562. // Continue looking for the path or an available position.
  9563. //
  9564. NextEntry = NextEntry->Flink;
  9565. }
  9566. //
  9567. // We will insert the path before NextEntry. First create an entry
  9568. // we can insert.
  9569. //
  9570. AllocationSize = sizeof(PFSVC_PATH);
  9571. AllocationSize += PathLength * sizeof(WCHAR);
  9572. //
  9573. // Note that PFSVC_PATH already contains space for the terminating
  9574. // NUL character.
  9575. //
  9576. if (PathList->Allocator) {
  9577. NewPathEntry = PfSvStringAllocatorAllocate(PathList->Allocator, AllocationSize);
  9578. } else {
  9579. NewPathEntry = PFSVC_ALLOC(AllocationSize);
  9580. }
  9581. if (!NewPathEntry) {
  9582. ErrorCode = ERROR_NOT_ENOUGH_MEMORY;
  9583. goto cleanup;
  9584. }
  9585. //
  9586. // Copy path and terminate it.
  9587. //
  9588. NewPathEntry->Length = PathLength;
  9589. RtlCopyMemory(NewPathEntry->Path,
  9590. Path,
  9591. PathLength * sizeof(WCHAR));
  9592. NewPathEntry->Path[PathLength] = 0;
  9593. //
  9594. // Insert it into the sorted list before the current entry.
  9595. //
  9596. InsertTailList(NextEntry, &NewPathEntry->SortedLink);
  9597. //
  9598. // Insert it at the end of in-order list.
  9599. //
  9600. InsertTailList(&PathList->InOrderList, &NewPathEntry->InOrderLink);
  9601. PathList->NumPaths++;
  9602. PathList->TotalLength += NewPathEntry->Length;
  9603. ErrorCode = ERROR_SUCCESS;
  9604. cleanup:
  9605. if (ErrorCode != ERROR_SUCCESS) {
  9606. if (NewPathEntry) {
  9607. if (PathList->Allocator) {
  9608. PfSvStringAllocatorFree(PathList->Allocator, NewPathEntry);
  9609. } else {
  9610. PFSVC_FREE(NewPathEntry);
  9611. }
  9612. }
  9613. }
  9614. return ErrorCode;
  9615. }
  9616. PPFSVC_PATH
  9617. PfSvGetNextPathSorted (
  9618. PPFSVC_PATH_LIST PathList,
  9619. PPFSVC_PATH CurrentPath
  9620. )
  9621. /*++
  9622. Routine Description:
  9623. This function is used to walk through paths in a path list in
  9624. lexically sorted order.
  9625. Arguments:
  9626. PathList - Pointer to list.
  9627. CurrentPath - The current path entry. The function will return the
  9628. next entry in the list. If this is NULL, the first entry in the
  9629. list is returned.
  9630. Return Value:
  9631. NULL - There are no more entries in the list.
  9632. or Pointer to next path in the list.
  9633. --*/
  9634. {
  9635. PLIST_ENTRY EndOfList;
  9636. PLIST_ENTRY NextEntry;
  9637. PPFSVC_PATH NextPath;
  9638. //
  9639. // Initialize locals.
  9640. //
  9641. EndOfList = &PathList->SortedList;
  9642. //
  9643. // Determine NextEntry based on whether CurrentPath is specified.
  9644. //
  9645. if (CurrentPath) {
  9646. NextEntry = CurrentPath->SortedLink.Flink;
  9647. } else {
  9648. NextEntry = PathList->SortedList.Flink;
  9649. }
  9650. //
  9651. // Check if the NextEntry points to the end of list.
  9652. //
  9653. if (NextEntry == EndOfList) {
  9654. NextPath = NULL;
  9655. } else {
  9656. NextPath = CONTAINING_RECORD(NextEntry,
  9657. PFSVC_PATH,
  9658. SortedLink);
  9659. }
  9660. return NextPath;
  9661. }
  9662. PPFSVC_PATH
  9663. PfSvGetNextPathInOrder (
  9664. PPFSVC_PATH_LIST PathList,
  9665. PPFSVC_PATH CurrentPath
  9666. )
  9667. /*++
  9668. Routine Description:
  9669. This function is used to walk through paths in a path list in
  9670. the order they were inserted into the list.
  9671. Arguments:
  9672. PathList - Pointer to list.
  9673. CurrentPath - The current path entry. The function will return the
  9674. next entry in the list. If this is NULL, the first entry in the
  9675. list is returned.
  9676. Return Value:
  9677. NULL - There are no more entries in the list.
  9678. or Pointer to next path in the list.
  9679. --*/
  9680. {
  9681. PLIST_ENTRY EndOfList;
  9682. PLIST_ENTRY NextEntry;
  9683. PPFSVC_PATH NextPath;
  9684. //
  9685. // Initialize locals.
  9686. //
  9687. EndOfList = &PathList->InOrderList;
  9688. //
  9689. // Determine NextEntry based on whether CurrentPath is specified.
  9690. //
  9691. if (CurrentPath) {
  9692. NextEntry = CurrentPath->InOrderLink.Flink;
  9693. } else {
  9694. NextEntry = PathList->InOrderList.Flink;
  9695. }
  9696. //
  9697. // Check if the NextEntry points to the end of list.
  9698. //
  9699. if (NextEntry == EndOfList) {
  9700. NextPath = NULL;
  9701. } else {
  9702. NextPath = CONTAINING_RECORD(NextEntry,
  9703. PFSVC_PATH,
  9704. InOrderLink);
  9705. }
  9706. return NextPath;
  9707. }
  9708. //
  9709. // Routines to build the list of files accessed by the boot loader.
  9710. //
  9711. DWORD
  9712. PfSvBuildBootLoaderFilesList (
  9713. PPFSVC_PATH_LIST PathList
  9714. )
  9715. /*++
  9716. Routine Description:
  9717. This function attempts to add the list of files loaded in the boot
  9718. loader to the specified file list.
  9719. Arguments:
  9720. PathList - Pointer to initialized list.
  9721. Return Value:
  9722. Win32 error code.
  9723. --*/
  9724. {
  9725. DWORD ErrorCode;
  9726. SC_HANDLE ScHandle;
  9727. SC_HANDLE ServiceHandle;
  9728. LPENUM_SERVICE_STATUS_PROCESS EnumBuffer;
  9729. LPENUM_SERVICE_STATUS_PROCESS ServiceInfo;
  9730. ULONG EnumBufferMaxSize;
  9731. ULONG NumResizes;
  9732. BOOL Result;
  9733. ULONG RequiredAdditionalSize;
  9734. ULONG RequiredSize;
  9735. ULONG NumServicesEnumerated;
  9736. ULONG ResumeHandle;
  9737. ULONG ServiceIdx;
  9738. LPQUERY_SERVICE_CONFIG ServiceConfigBuffer;
  9739. ULONG ServiceConfigBufferMaxSize;
  9740. WCHAR FilePath[MAX_PATH + 1];
  9741. ULONG FilePathLength;
  9742. ULONG SystemDirLength;
  9743. ULONG RequiredLength;
  9744. WCHAR *KernelName;
  9745. WCHAR *HalName;
  9746. WCHAR *SystemHive;
  9747. WCHAR *SoftwareHive;
  9748. //
  9749. // Initialize locals.
  9750. //
  9751. ScHandle = NULL;
  9752. ServiceHandle = NULL;
  9753. EnumBuffer = NULL;
  9754. EnumBufferMaxSize = 0;
  9755. NumServicesEnumerated = 0;
  9756. ServiceConfigBuffer = NULL;
  9757. ServiceConfigBufferMaxSize = 0;
  9758. KernelName = L"ntoskrnl.exe";
  9759. HalName = L"hal.dll";
  9760. SystemHive = L"config\\system";
  9761. SoftwareHive = L"config\\software";
  9762. //
  9763. // Add kernel & hal to known files list:
  9764. //
  9765. //
  9766. // Get path to system directory.
  9767. //
  9768. SystemDirLength = GetSystemDirectory(FilePath, MAX_PATH);
  9769. if (!SystemDirLength) {
  9770. ErrorCode = GetLastError();
  9771. goto cleanup;
  9772. }
  9773. //
  9774. // Append a trailing \.
  9775. //
  9776. if (SystemDirLength + 1 < MAX_PATH) {
  9777. FilePath[SystemDirLength] = '\\';
  9778. SystemDirLength++;
  9779. FilePath[SystemDirLength] = 0;
  9780. } else {
  9781. ErrorCode = ERROR_INSUFFICIENT_BUFFER;
  9782. goto cleanup;
  9783. }
  9784. //
  9785. // Append kernel name and add it to the list.
  9786. //
  9787. FilePathLength = SystemDirLength;
  9788. FilePathLength += wcslen(KernelName);
  9789. if (FilePathLength < MAX_PATH) {
  9790. wcscat(FilePath, KernelName);
  9791. ErrorCode = PfSvAddBootImageAndImportsToList(PathList,
  9792. FilePath,
  9793. FilePathLength);
  9794. if (ErrorCode != ERROR_SUCCESS) {
  9795. goto cleanup;
  9796. }
  9797. } else {
  9798. ErrorCode = ERROR_INSUFFICIENT_BUFFER;
  9799. goto cleanup;
  9800. }
  9801. //
  9802. // Roll FilePath back to system directory. Append hal name and add
  9803. // it to the list.
  9804. //
  9805. FilePathLength = SystemDirLength;
  9806. FilePathLength += wcslen(HalName);
  9807. if (FilePathLength < MAX_PATH) {
  9808. FilePath[SystemDirLength] = 0;
  9809. wcscat(FilePath, HalName);
  9810. ErrorCode = PfSvAddBootImageAndImportsToList(PathList,
  9811. FilePath,
  9812. FilePathLength);
  9813. if (ErrorCode != ERROR_SUCCESS) {
  9814. goto cleanup;
  9815. }
  9816. } else {
  9817. ErrorCode = ERROR_INSUFFICIENT_BUFFER;
  9818. goto cleanup;
  9819. }
  9820. //
  9821. // Roll FilePath back to system directory. Append system hive path
  9822. // and add it to the list.
  9823. //
  9824. FilePathLength = SystemDirLength;
  9825. FilePathLength += wcslen(SystemHive);
  9826. if (FilePathLength < MAX_PATH) {
  9827. FilePath[SystemDirLength] = 0;
  9828. wcscat(FilePath, SystemHive);
  9829. ErrorCode = PfSvAddToPathList(PathList,
  9830. FilePath,
  9831. FilePathLength);
  9832. if (ErrorCode != ERROR_SUCCESS) {
  9833. goto cleanup;
  9834. }
  9835. } else {
  9836. ErrorCode = ERROR_INSUFFICIENT_BUFFER;
  9837. goto cleanup;
  9838. }
  9839. //
  9840. // Roll FilePath back to system directory. Append software hive path
  9841. // and add it to the list.
  9842. //
  9843. FilePathLength = SystemDirLength;
  9844. FilePathLength += wcslen(SoftwareHive);
  9845. if (FilePathLength < MAX_PATH) {
  9846. FilePath[SystemDirLength] = 0;
  9847. wcscat(FilePath, SoftwareHive);
  9848. ErrorCode = PfSvAddToPathList(PathList,
  9849. FilePath,
  9850. FilePathLength);
  9851. if (ErrorCode != ERROR_SUCCESS) {
  9852. goto cleanup;
  9853. }
  9854. } else {
  9855. ErrorCode = ERROR_INSUFFICIENT_BUFFER;
  9856. goto cleanup;
  9857. }
  9858. //
  9859. // Note that we will use FilePath & FilePathLength to add the
  9860. // software hive after we add all the other boot loader files. The
  9861. // software hive is not accessed in the boot loader, but during
  9862. // boot. It is not put into the boot scenario file, however. We
  9863. // don't want to mix it in with the boot loader files, so we don't
  9864. // hurt the boot loader performance.
  9865. //
  9866. //
  9867. // Add file paths for NLS data & fonts loaded by the boot loader.
  9868. //
  9869. PfSvGetBootLoaderNlsFileNames(PathList);
  9870. //
  9871. // Open service controller.
  9872. //
  9873. // FUTURE-2002/03/29-ScottMa -- The dwDesiredAccess parameter may be able
  9874. // to be set to a lesser set of priveleges.
  9875. ScHandle = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
  9876. if (ScHandle == NULL) {
  9877. ErrorCode = GetLastError();
  9878. goto cleanup;
  9879. }
  9880. //
  9881. // Get the list of boot services we are interested in.
  9882. //
  9883. NumResizes = 0;
  9884. do {
  9885. //
  9886. // We want to get all of services at one call.
  9887. //
  9888. ResumeHandle = 0;
  9889. Result = EnumServicesStatusEx (ScHandle,
  9890. SC_ENUM_PROCESS_INFO,
  9891. SERVICE_DRIVER,
  9892. SERVICE_ACTIVE,
  9893. (PVOID)EnumBuffer,
  9894. EnumBufferMaxSize,
  9895. &RequiredAdditionalSize,
  9896. &NumServicesEnumerated,
  9897. &ResumeHandle,
  9898. NULL);
  9899. if (Result) {
  9900. //
  9901. // We got it.
  9902. //
  9903. break;
  9904. }
  9905. //
  9906. // Check why our call failed.
  9907. //
  9908. ErrorCode = GetLastError();
  9909. //
  9910. // If we failed for some other reason than that our buffer was
  9911. // too small, we cannot go on.
  9912. //
  9913. if (ErrorCode != ERROR_MORE_DATA) {
  9914. goto cleanup;
  9915. }
  9916. //
  9917. // Free the old buffer if it exists, and allocate a bigger one.
  9918. //
  9919. RequiredSize = EnumBufferMaxSize + RequiredAdditionalSize;
  9920. if (EnumBuffer) {
  9921. PFSVC_FREE(EnumBuffer);
  9922. EnumBuffer = NULL;
  9923. EnumBufferMaxSize = 0;
  9924. }
  9925. EnumBuffer = PFSVC_ALLOC(RequiredSize);
  9926. if (EnumBuffer == NULL) {
  9927. ErrorCode = ERROR_NOT_ENOUGH_MEMORY;
  9928. goto cleanup;
  9929. }
  9930. EnumBufferMaxSize = RequiredSize;
  9931. //
  9932. // Make sure we don't loop for ever.
  9933. //
  9934. NumResizes++;
  9935. if (NumResizes > 100) {
  9936. ErrorCode = ERROR_INVALID_FUNCTION;
  9937. goto cleanup;
  9938. }
  9939. } while (TRUE);
  9940. //
  9941. // Identify the enumerated services that may be loaded by the boot
  9942. // loader.
  9943. //
  9944. for (ServiceIdx = 0; ServiceIdx < NumServicesEnumerated; ServiceIdx++) {
  9945. ServiceInfo = &EnumBuffer[ServiceIdx];
  9946. //
  9947. // Open the service to get its configuration info.
  9948. //
  9949. ServiceHandle = OpenService(ScHandle,
  9950. ServiceInfo->lpServiceName,
  9951. SERVICE_QUERY_CONFIG);
  9952. if (ServiceHandle == NULL) {
  9953. ErrorCode = GetLastError();
  9954. goto cleanup;
  9955. }
  9956. //
  9957. // Query service configuration.
  9958. //
  9959. NumResizes = 0;
  9960. do {
  9961. Result = QueryServiceConfig(ServiceHandle,
  9962. ServiceConfigBuffer,
  9963. ServiceConfigBufferMaxSize,
  9964. &RequiredSize);
  9965. if (Result) {
  9966. //
  9967. // We got it.
  9968. //
  9969. break;
  9970. }
  9971. ErrorCode = GetLastError();
  9972. if (ErrorCode != ERROR_INSUFFICIENT_BUFFER) {
  9973. //
  9974. // This is a real error.
  9975. //
  9976. goto cleanup;
  9977. }
  9978. //
  9979. // Resize the buffer and try again.
  9980. //
  9981. if (ServiceConfigBuffer) {
  9982. PFSVC_FREE(ServiceConfigBuffer);
  9983. ServiceConfigBuffer = NULL;
  9984. ServiceConfigBufferMaxSize = 0;
  9985. }
  9986. ServiceConfigBuffer = PFSVC_ALLOC(RequiredSize);
  9987. if (ServiceConfigBuffer == NULL) {
  9988. ErrorCode = ERROR_NOT_ENOUGH_MEMORY;
  9989. goto cleanup;
  9990. }
  9991. ServiceConfigBufferMaxSize = RequiredSize;
  9992. //
  9993. // Make sure we don't loop forever.
  9994. //
  9995. NumResizes++;
  9996. if (NumResizes > 100) {
  9997. ErrorCode = ERROR_INVALID_FUNCTION;
  9998. goto cleanup;
  9999. }
  10000. } while (TRUE);
  10001. //
  10002. // We are interested in this service only if it starts as a
  10003. // boot driver or if it is a file system.
  10004. //
  10005. if (ServiceConfigBuffer->dwStartType == SERVICE_BOOT_START ||
  10006. ServiceConfigBuffer->dwServiceType == SERVICE_FILE_SYSTEM_DRIVER) {
  10007. //
  10008. // Try to locate the real service binary path.
  10009. //
  10010. ErrorCode = PfSvGetBootServiceFullPath(ServiceInfo->lpServiceName,
  10011. ServiceConfigBuffer->lpBinaryPathName,
  10012. FilePath,
  10013. MAX_PATH,
  10014. &RequiredLength);
  10015. if (ErrorCode == ERROR_SUCCESS) {
  10016. PfSvAddBootImageAndImportsToList(PathList,
  10017. FilePath,
  10018. wcslen(FilePath));
  10019. }
  10020. }
  10021. //
  10022. // Close the handle and continue.
  10023. //
  10024. CloseServiceHandle(ServiceHandle);
  10025. ServiceHandle = NULL;
  10026. }
  10027. ErrorCode = ERROR_SUCCESS;
  10028. cleanup:
  10029. if (ServiceHandle) {
  10030. CloseServiceHandle(ServiceHandle);
  10031. }
  10032. if (ScHandle) {
  10033. CloseServiceHandle(ScHandle);
  10034. }
  10035. if (EnumBuffer) {
  10036. PFSVC_FREE(EnumBuffer);
  10037. }
  10038. if (ServiceConfigBuffer) {
  10039. PFSVC_FREE(ServiceConfigBuffer);
  10040. }
  10041. return ErrorCode;
  10042. }
  10043. DWORD
  10044. PfSvAddBootImageAndImportsToList(
  10045. PPFSVC_PATH_LIST PathList,
  10046. WCHAR *FilePath,
  10047. ULONG FilePathLength
  10048. )
  10049. /*++
  10050. Routine Description:
  10051. This function attempts to add the image file whose fully qualified
  10052. path is in FilePath as well as the modules it imports from to the
  10053. file list, if those modules can be located.
  10054. NOTE: Ntoskrnl.exe and Hal.dll are special cased out and not added
  10055. to the file list, since most drivers will import from them. They
  10056. can be added to the list seperately. Also note that the file list
  10057. is not checked for duplicates when adding new entries.
  10058. Arguments:
  10059. PathList - Pointer to list.
  10060. FilePath - Fully qualified path of an image file.
  10061. FilePathLength - Length of the file path in characters excluding NUL.
  10062. Return Value:
  10063. Win32 error code.
  10064. --*/
  10065. {
  10066. DWORD ErrorCode;
  10067. ULONG MaxNumImports;
  10068. ULONG NumImports;
  10069. WCHAR **ImportNames;
  10070. ULONG ImportIdx;
  10071. ULONG BufferSize;
  10072. WCHAR *FileName;
  10073. WCHAR ParentDir[MAX_PATH + 1];
  10074. ULONG ParentDirLength;
  10075. WCHAR *ImportName;
  10076. ULONG ImportNameLength;
  10077. WCHAR ImportPath[MAX_PATH + 1];
  10078. PUCHAR ImportBase;
  10079. ULONG RequiredLength;
  10080. ULONG FileSize;
  10081. PIMAGE_IMPORT_DESCRIPTOR NewImportDescriptor;
  10082. CHAR *NewImportNameAnsi;
  10083. WCHAR *NewImportName;
  10084. ULONG NewImportNameRva;
  10085. BOOLEAN AddedToTable;
  10086. ULONG ImportTableSize;
  10087. PIMAGE_NT_HEADERS NtHeaders;
  10088. ULONG NextImport;
  10089. ULONG FailedCheck;
  10090. //
  10091. // Initialize locals.
  10092. //
  10093. MaxNumImports = 256;
  10094. ImportNames = NULL;
  10095. NumImports = 0;
  10096. NextImport = 0;
  10097. //
  10098. // Find the file name from the path.
  10099. //
  10100. if (FilePathLength == 0 || FilePath[FilePathLength - 1] == L'\\') {
  10101. ErrorCode = ERROR_BAD_LENGTH;
  10102. goto cleanup;
  10103. }
  10104. FileName = &FilePath[FilePathLength - 1];
  10105. while (FileName > FilePath) {
  10106. //
  10107. // It is OK to decrement FileName the first time in the loop because
  10108. // we checked for the terminating slash above.
  10109. //
  10110. FileName--;
  10111. if (*FileName == L'\\') {
  10112. FileName++;
  10113. break;
  10114. }
  10115. }
  10116. //
  10117. // Extract the parent directory.
  10118. //
  10119. ParentDirLength = (ULONG) (FileName - FilePath);
  10120. if (ParentDirLength >= MAX_PATH) {
  10121. ErrorCode = ERROR_BAD_LENGTH;
  10122. goto cleanup;
  10123. }
  10124. wcsncpy(ParentDir, FilePath, ParentDirLength);
  10125. ParentDir[ParentDirLength] = 0;
  10126. //
  10127. // Allocate a table for keeping track of imported modules.
  10128. //
  10129. BufferSize = MaxNumImports * sizeof(WCHAR *);
  10130. ImportNames = PFSVC_ALLOC(BufferSize);
  10131. if (ImportNames == NULL) {
  10132. ErrorCode = ERROR_NOT_ENOUGH_MEMORY;
  10133. goto cleanup;
  10134. }
  10135. RtlZeroMemory(ImportNames, BufferSize);
  10136. //
  10137. // Insert the file into the table and kick off import enumeration
  10138. // on the table. Each enumerated import gets appended to the table
  10139. // if it is not already present. Enumeration continues until all
  10140. // appended entries are processed.
  10141. //
  10142. ImportNames[NumImports] = FileName;
  10143. NumImports++;
  10144. while (NextImport < NumImports) {
  10145. //
  10146. // Initialize loop locals.
  10147. //
  10148. ImportBase = NULL;
  10149. ImportName = ImportNames[NextImport];
  10150. ImportNameLength = wcslen(ImportName);
  10151. //
  10152. // Locate the file. First look in ParentDir.
  10153. //
  10154. if (ImportNameLength + ParentDirLength >= MAX_PATH) {
  10155. goto NextImport;
  10156. }
  10157. wcscpy(ImportPath, ParentDir);
  10158. wcscat(ImportPath, ImportName);
  10159. if (GetFileAttributes(ImportPath) == INVALID_FILE_ATTRIBUTES) {
  10160. //
  10161. // Look for this file in other known directories.
  10162. //
  10163. ErrorCode = PfSvLocateBootServiceFile(ImportName,
  10164. ImportNameLength,
  10165. ImportPath,
  10166. MAX_PATH,
  10167. &RequiredLength);
  10168. if (ErrorCode != ERROR_SUCCESS) {
  10169. goto NextImport;
  10170. }
  10171. }
  10172. //
  10173. // Add the file to the file list.
  10174. //
  10175. PfSvAddToPathList(PathList,
  10176. ImportPath,
  10177. wcslen(ImportPath));
  10178. //
  10179. // Map the file.
  10180. //
  10181. ErrorCode = PfSvGetViewOfFile(ImportPath, &ImportBase, &FileSize);
  10182. if (ErrorCode != ERROR_SUCCESS) {
  10183. goto NextImport;
  10184. }
  10185. //
  10186. // Make sure this is an image file.
  10187. //
  10188. __try {
  10189. //
  10190. // This is the first access to the mapped file. Under stress we might not be
  10191. // able to page this in and an exception might be raised. This protects us from
  10192. // the most common failure case.
  10193. //
  10194. //
  10195. // Verify the image and its import table. If the image is corrupt it might
  10196. // result in an AV when we try to walk its imports.
  10197. //
  10198. FailedCheck = PfVerifyImageImportTable(ImportBase, FileSize, FALSE);
  10199. if (FailedCheck) {
  10200. ErrorCode = ERROR_BAD_FORMAT;
  10201. goto NextImport;
  10202. }
  10203. NtHeaders = ImageNtHeader(ImportBase);
  10204. } __except (EXCEPTION_EXECUTE_HANDLER) {
  10205. NtHeaders = NULL;
  10206. }
  10207. if (NtHeaders == NULL) {
  10208. goto NextImport;
  10209. }
  10210. //
  10211. // Walk through the imports for this binary.
  10212. //
  10213. NewImportDescriptor = ImageDirectoryEntryToData(ImportBase,
  10214. FALSE,
  10215. IMAGE_DIRECTORY_ENTRY_IMPORT,
  10216. &ImportTableSize);
  10217. while (NewImportDescriptor &&
  10218. (NewImportDescriptor->Name != 0) &&
  10219. (NewImportDescriptor->FirstThunk != 0)) {
  10220. //
  10221. // Initialize loop locals.
  10222. //
  10223. AddedToTable = FALSE;
  10224. NewImportName = NULL;
  10225. //
  10226. // Get the name for this import.
  10227. //
  10228. NewImportNameRva = NewImportDescriptor->Name;
  10229. NewImportNameAnsi = ImageRvaToVa(NtHeaders,
  10230. ImportBase,
  10231. NewImportNameRva,
  10232. NULL);
  10233. ErrorCode = GetLastError();
  10234. if (NewImportNameAnsi) {
  10235. NewImportName = PfSvcAnsiToUnicode(NewImportNameAnsi);
  10236. }
  10237. if (NewImportName == NULL) {
  10238. goto NextImportDescriptor;
  10239. }
  10240. //
  10241. // Skip the kernel and hal imports. See comment in
  10242. // function description.
  10243. //
  10244. if (!_wcsicmp(NewImportName, L"ntoskrnl.exe") ||
  10245. !_wcsicmp(NewImportName, L"hal.dll")) {
  10246. goto NextImportDescriptor;
  10247. }
  10248. //
  10249. // Check to see if this import is already in our table.
  10250. //
  10251. for (ImportIdx = 0; ImportIdx < NumImports; ImportIdx++) {
  10252. if (!_wcsicmp(NewImportName, ImportNames[ImportIdx])) {
  10253. goto NextImportDescriptor;
  10254. }
  10255. }
  10256. //
  10257. // Append this import to the table.
  10258. //
  10259. if (NumImports < MaxNumImports) {
  10260. ImportNames[NumImports] = NewImportName;
  10261. NumImports++;
  10262. AddedToTable = TRUE;
  10263. }
  10264. NextImportDescriptor:
  10265. if (!AddedToTable && NewImportName) {
  10266. PFSVC_FREE(NewImportName);
  10267. }
  10268. if (NumImports >= MaxNumImports) {
  10269. break;
  10270. }
  10271. NewImportDescriptor++;
  10272. }
  10273. NextImport:
  10274. if (ImportBase) {
  10275. UnmapViewOfFile(ImportBase);
  10276. }
  10277. NextImport++;
  10278. }
  10279. ErrorCode = ERROR_SUCCESS;
  10280. cleanup:
  10281. if (ImportNames) {
  10282. //
  10283. // The first entry in the table is the filename from FilePath,
  10284. // which is not allocated and which should not be freed.
  10285. //
  10286. for (ImportIdx = 1; ImportIdx < NumImports; ImportIdx++) {
  10287. PfSvcFreeString(ImportNames[ImportIdx]);
  10288. }
  10289. PFSVC_FREE(ImportNames);
  10290. }
  10291. return ErrorCode;
  10292. }
  10293. DWORD
  10294. PfSvLocateBootServiceFile(
  10295. IN WCHAR *FileName,
  10296. IN ULONG FileNameLength,
  10297. OUT WCHAR *FullPathBuffer,
  10298. IN ULONG FullPathBufferLength,
  10299. OUT PULONG RequiredLength
  10300. )
  10301. /*++
  10302. Routine Description:
  10303. This function looks at known directories in an *attempt* locate
  10304. the file whose name is specified. The logic may have to be
  10305. improved.
  10306. Arguments:
  10307. FileName - File name to look for.
  10308. FileNameLength - Length of file name in characters excluding NUL.
  10309. FullPathBuffer - The full path will be put here.
  10310. FullPathBufferLength - Length of the FullPathBuffer in characters.
  10311. RequiredLength - If FullPathBuffer is too small, this is how big it
  10312. should be in characters.
  10313. Return Value:
  10314. ERROR_INSUFFICIENT_BUFFER - The FullPathBuffer is not big enough.
  10315. Win32 error code.
  10316. --*/
  10317. {
  10318. DWORD ErrorCode;
  10319. WCHAR *DriversDirName;
  10320. ULONG SystemDirLength;
  10321. //
  10322. // Initialize locals.
  10323. //
  10324. DriversDirName = L"drivers\\";
  10325. //
  10326. // Copy system root path and a trailing \.
  10327. //
  10328. SystemDirLength = GetSystemDirectory(FullPathBuffer, FullPathBufferLength);
  10329. if (!SystemDirLength) {
  10330. ErrorCode = GetLastError();
  10331. goto cleanup;
  10332. }
  10333. SystemDirLength++;
  10334. //
  10335. // Calculate maximum size of required length.
  10336. //
  10337. (*RequiredLength) = SystemDirLength;
  10338. (*RequiredLength) += wcslen(DriversDirName);
  10339. (*RequiredLength) += FileNameLength;
  10340. (*RequiredLength) += 1; // terminating NUL.
  10341. if ((*RequiredLength) > FullPathBufferLength) {
  10342. ErrorCode = ERROR_INSUFFICIENT_BUFFER;
  10343. goto cleanup;
  10344. }
  10345. //
  10346. // Append slash.
  10347. //
  10348. wcscat(FullPathBuffer, L"\\");
  10349. //
  10350. // Append drivers path.
  10351. //
  10352. wcscat(FullPathBuffer, DriversDirName);
  10353. //
  10354. // Append file name.
  10355. //
  10356. wcscat(FullPathBuffer, FileName);
  10357. if (GetFileAttributes(FullPathBuffer) != INVALID_FILE_ATTRIBUTES) {
  10358. ErrorCode = ERROR_SUCCESS;
  10359. goto cleanup;
  10360. }
  10361. //
  10362. // Roll back and look for the file in the system
  10363. // directory. SystemDirLength includes the slash after system
  10364. // directory path.
  10365. //
  10366. FullPathBuffer[SystemDirLength] = 0;
  10367. wcscat(FullPathBuffer, FileName);
  10368. if (GetFileAttributes(FullPathBuffer) != INVALID_FILE_ATTRIBUTES) {
  10369. ErrorCode = ERROR_SUCCESS;
  10370. goto cleanup;
  10371. }
  10372. ErrorCode = ERROR_FILE_NOT_FOUND;
  10373. cleanup:
  10374. return ErrorCode;
  10375. }
  10376. DWORD
  10377. PfSvGetBootServiceFullPath(
  10378. IN WCHAR *ServiceName,
  10379. IN WCHAR *BinaryPathName,
  10380. OUT WCHAR *FullPathBuffer,
  10381. IN ULONG FullPathBufferLength,
  10382. OUT PULONG RequiredLength
  10383. )
  10384. /*++
  10385. Routine Description:
  10386. This function *attempts* to locate specified boot service. The
  10387. logic may have to be improved.
  10388. Arguments:
  10389. ServiceName - Name of the service.
  10390. BinaryPathName - From service configuration info. This is supposed
  10391. to be the full path, but it is not.
  10392. FullPathBuffer - The full path will be put here.
  10393. FullPathBufferLength - Length of the FullPathBuffer in characters.
  10394. RequiredLength - If FullPathBuffer is too small, this is how big it
  10395. should be in characters.
  10396. Return Value:
  10397. Win32 error code.
  10398. --*/
  10399. {
  10400. DWORD ErrorCode;
  10401. WCHAR FileName[MAX_PATH];
  10402. ULONG BinaryPathLength;
  10403. BOOLEAN GotFileNameFromBinaryPath;
  10404. LONG CharIdx;
  10405. ULONG CopyLength;
  10406. WCHAR *SysExtension;
  10407. WCHAR *DllExtension;
  10408. WCHAR *FileNamePart;
  10409. //
  10410. // Initialize locals.
  10411. //
  10412. GotFileNameFromBinaryPath = FALSE;
  10413. SysExtension = L".sys";
  10414. DllExtension = L".dll";
  10415. //
  10416. // Check if a binary path was specified.
  10417. //
  10418. if (BinaryPathName && BinaryPathName[0]) {
  10419. //
  10420. // See if the file is really there.
  10421. //
  10422. if (GetFileAttributes(BinaryPathName) != INVALID_FILE_ATTRIBUTES) {
  10423. //
  10424. // BinaryPathName may not be a fully qualified path. Make
  10425. // sure it is.
  10426. //
  10427. (*RequiredLength) = GetFullPathName(BinaryPathName,
  10428. FullPathBufferLength,
  10429. FullPathBuffer,
  10430. &FileNamePart);
  10431. if ((*RequiredLength) == 0) {
  10432. ErrorCode = GetLastError();
  10433. goto cleanup;
  10434. }
  10435. if ((*RequiredLength) > FullPathBufferLength) {
  10436. ErrorCode = ERROR_INSUFFICIENT_BUFFER;
  10437. goto cleanup;
  10438. }
  10439. ErrorCode = ERROR_SUCCESS;
  10440. goto cleanup;
  10441. }
  10442. //
  10443. // Try to extract a file name from the binary path.
  10444. //
  10445. BinaryPathLength = wcslen(BinaryPathName);
  10446. for (CharIdx = BinaryPathLength - 1;
  10447. CharIdx >= 0;
  10448. CharIdx --) {
  10449. if (BinaryPathName[CharIdx] == L'\\') {
  10450. //
  10451. // Check length and copy it.
  10452. //
  10453. CopyLength = BinaryPathLength - CharIdx;
  10454. if (CopyLength < MAX_PATH &&
  10455. CopyLength > 1) {
  10456. //
  10457. // Copy name starting after the \ character.
  10458. //
  10459. wcscpy(FileName, &BinaryPathName[CharIdx + 1]);
  10460. GotFileNameFromBinaryPath = TRUE;
  10461. }
  10462. break;
  10463. }
  10464. }
  10465. //
  10466. // There was not a slash. Maybe the BinaryPathLength is just
  10467. // the file name.
  10468. //
  10469. if (GotFileNameFromBinaryPath == FALSE &&
  10470. BinaryPathLength &&
  10471. BinaryPathLength < MAX_PATH) {
  10472. wcscpy(FileName, BinaryPathName);
  10473. GotFileNameFromBinaryPath = TRUE;
  10474. }
  10475. }
  10476. //
  10477. // After this point we will base our search on file name hints.
  10478. //
  10479. //
  10480. // If we got a file name from the binary path try that first.
  10481. //
  10482. if (GotFileNameFromBinaryPath) {
  10483. ErrorCode = PfSvLocateBootServiceFile(FileName,
  10484. wcslen(FileName),
  10485. FullPathBuffer,
  10486. FullPathBufferLength,
  10487. RequiredLength);
  10488. if (ErrorCode != ERROR_FILE_NOT_FOUND) {
  10489. //
  10490. // If we found a path or if the buffer length was not
  10491. // enough we will bubble up that to our caller.
  10492. //
  10493. goto cleanup;
  10494. }
  10495. }
  10496. //
  10497. // Build a file name from service name by appending a .sys.
  10498. //
  10499. CopyLength = wcslen(ServiceName);
  10500. CopyLength += wcslen(SysExtension);
  10501. if (CopyLength >= MAX_PATH) {
  10502. //
  10503. // The service name is too long!
  10504. //
  10505. ErrorCode = ERROR_BAD_FORMAT;
  10506. goto cleanup;
  10507. }
  10508. wcscpy(FileName, ServiceName);
  10509. wcscat(FileName, SysExtension);
  10510. ErrorCode = PfSvLocateBootServiceFile(FileName,
  10511. wcslen(FileName),
  10512. FullPathBuffer,
  10513. FullPathBufferLength,
  10514. RequiredLength);
  10515. if (ErrorCode != ERROR_FILE_NOT_FOUND) {
  10516. //
  10517. // If we found a path or if the buffer length was not
  10518. // enough we will bubble up that to our caller.
  10519. //
  10520. goto cleanup;
  10521. }
  10522. //
  10523. // Build a file name from service name by appending a .dll.
  10524. //
  10525. CopyLength = wcslen(ServiceName);
  10526. CopyLength += wcslen(DllExtension);
  10527. if (CopyLength >= MAX_PATH) {
  10528. //
  10529. // The service name is too long!
  10530. //
  10531. ErrorCode = ERROR_BAD_FORMAT;
  10532. goto cleanup;
  10533. }
  10534. wcscpy(FileName, ServiceName);
  10535. wcscat(FileName, DllExtension);
  10536. ErrorCode = PfSvLocateBootServiceFile(FileName,
  10537. wcslen(FileName),
  10538. FullPathBuffer,
  10539. FullPathBufferLength,
  10540. RequiredLength);
  10541. if (ErrorCode != ERROR_FILE_NOT_FOUND) {
  10542. //
  10543. // If we found a path or if the buffer length was not
  10544. // enough we will bubble up that to our caller.
  10545. //
  10546. goto cleanup;
  10547. }
  10548. //
  10549. // We could not find the file...
  10550. //
  10551. ErrorCode = ERROR_FILE_NOT_FOUND;
  10552. cleanup:
  10553. return ErrorCode;
  10554. }
  10555. DWORD
  10556. PfSvGetBootLoaderNlsFileNames (
  10557. PPFSVC_PATH_LIST PathList
  10558. )
  10559. /*++
  10560. Routine Description:
  10561. This function attempts to add the list of NLS files loaded in the
  10562. boot loader to the specified file list.
  10563. Arguments:
  10564. PathList - Pointer to list.
  10565. Return Value:
  10566. Win32 error code.
  10567. --*/
  10568. {
  10569. DWORD ErrorCode;
  10570. HKEY NlsKeyHandle;
  10571. WCHAR *CodePageKeyName;
  10572. HKEY CodePageKeyHandle;
  10573. WCHAR *LanguageKeyName;
  10574. HKEY LanguageKeyHandle;
  10575. ULONG BufferSize;
  10576. ULONG RequiredSize;
  10577. ULONG RequiredLength;
  10578. WCHAR FileName[MAX_PATH + 1];
  10579. WCHAR FilePath[MAX_PATH + 1];
  10580. WCHAR *AnsiCodePageName;
  10581. WCHAR *OemCodePageName;
  10582. WCHAR *OemHalName;
  10583. WCHAR *DefaultLangName;
  10584. ULONG RegValueType;
  10585. //
  10586. // Initialize locals.
  10587. //
  10588. NlsKeyHandle = NULL;
  10589. CodePageKeyHandle = NULL;
  10590. LanguageKeyHandle = NULL;
  10591. CodePageKeyName = L"CodePage";
  10592. LanguageKeyName = L"Language";
  10593. AnsiCodePageName = L"ACP";
  10594. OemCodePageName = L"OEMCP";
  10595. DefaultLangName = L"Default";
  10596. OemHalName = L"OEMHAL";
  10597. //
  10598. // Open NLS key.
  10599. //
  10600. ErrorCode = RegOpenKey(HKEY_LOCAL_MACHINE,
  10601. PFSVC_NLS_REG_KEY_PATH,
  10602. &NlsKeyHandle);
  10603. if (ErrorCode != ERROR_SUCCESS) {
  10604. goto cleanup;
  10605. }
  10606. //
  10607. // Open CodePage key.
  10608. //
  10609. ErrorCode = RegOpenKey(NlsKeyHandle,
  10610. CodePageKeyName,
  10611. &CodePageKeyHandle);
  10612. if (ErrorCode != ERROR_SUCCESS) {
  10613. goto cleanup;
  10614. }
  10615. //
  10616. // Open Language key.
  10617. //
  10618. ErrorCode = RegOpenKey(NlsKeyHandle,
  10619. LanguageKeyName,
  10620. &LanguageKeyHandle);
  10621. if (ErrorCode != ERROR_SUCCESS) {
  10622. goto cleanup;
  10623. }
  10624. //
  10625. // AnsiCodePage:
  10626. //
  10627. ErrorCode = PfSvQueryNlsFileName(CodePageKeyHandle,
  10628. AnsiCodePageName,
  10629. FileName,
  10630. MAX_PATH * sizeof(WCHAR),
  10631. &RequiredSize);
  10632. if (ErrorCode == ERROR_SUCCESS) {
  10633. ErrorCode = PfSvLocateNlsFile(FileName,
  10634. FilePath,
  10635. MAX_PATH,
  10636. &RequiredLength);
  10637. if (ErrorCode == ERROR_SUCCESS) {
  10638. ErrorCode = PfSvAddToPathList(PathList, FilePath, wcslen(FilePath));
  10639. if (ErrorCode != ERROR_SUCCESS) {
  10640. goto cleanup;
  10641. }
  10642. }
  10643. }
  10644. //
  10645. // OemCodePage:
  10646. //
  10647. ErrorCode = PfSvQueryNlsFileName(CodePageKeyHandle,
  10648. OemCodePageName,
  10649. FileName,
  10650. MAX_PATH * sizeof(WCHAR),
  10651. &RequiredSize);
  10652. if (ErrorCode == ERROR_SUCCESS) {
  10653. ErrorCode = PfSvLocateNlsFile(FileName,
  10654. FilePath,
  10655. MAX_PATH,
  10656. &RequiredLength);
  10657. if (ErrorCode == ERROR_SUCCESS) {
  10658. ErrorCode = PfSvAddToPathList(PathList, FilePath, wcslen(FilePath));
  10659. if (ErrorCode != ERROR_SUCCESS) {
  10660. goto cleanup;
  10661. }
  10662. }
  10663. }
  10664. //
  10665. // Default language case conversion.
  10666. //
  10667. ErrorCode = PfSvQueryNlsFileName(LanguageKeyHandle,
  10668. DefaultLangName,
  10669. FileName,
  10670. MAX_PATH * sizeof(WCHAR),
  10671. &RequiredSize);
  10672. if (ErrorCode == ERROR_SUCCESS) {
  10673. ErrorCode = PfSvLocateNlsFile(FileName,
  10674. FilePath,
  10675. MAX_PATH,
  10676. &RequiredLength);
  10677. if (ErrorCode == ERROR_SUCCESS) {
  10678. ErrorCode = PfSvAddToPathList(PathList, FilePath, wcslen(FilePath));
  10679. if (ErrorCode != ERROR_SUCCESS) {
  10680. goto cleanup;
  10681. }
  10682. }
  10683. }
  10684. //
  10685. // OemHal:
  10686. //
  10687. BufferSize = MAX_PATH * sizeof(WCHAR);
  10688. ErrorCode = RegQueryValueEx(CodePageKeyHandle,
  10689. OemHalName,
  10690. NULL,
  10691. &RegValueType,
  10692. (PVOID) FileName,
  10693. &BufferSize);
  10694. FileName[MAX_PATH - 1] = 0;
  10695. if (ErrorCode == ERROR_SUCCESS && RegValueType == REG_SZ) {
  10696. ErrorCode = PfSvLocateNlsFile(FileName,
  10697. FilePath,
  10698. MAX_PATH,
  10699. &RequiredLength);
  10700. if (ErrorCode == ERROR_SUCCESS) {
  10701. ErrorCode = PfSvAddToPathList(PathList, FilePath, wcslen(FilePath));
  10702. if (ErrorCode != ERROR_SUCCESS) {
  10703. goto cleanup;
  10704. }
  10705. }
  10706. }
  10707. ErrorCode = ERROR_SUCCESS;
  10708. cleanup:
  10709. if (NlsKeyHandle) {
  10710. RegCloseKey(NlsKeyHandle);
  10711. }
  10712. if (CodePageKeyHandle) {
  10713. RegCloseKey(CodePageKeyHandle);
  10714. }
  10715. if (LanguageKeyHandle) {
  10716. RegCloseKey(LanguageKeyHandle);
  10717. }
  10718. return ErrorCode;
  10719. }
  10720. DWORD
  10721. PfSvLocateNlsFile(
  10722. WCHAR *FileName,
  10723. WCHAR *FilePathBuffer,
  10724. ULONG FilePathBufferLength,
  10725. ULONG *RequiredLength
  10726. )
  10727. /*++
  10728. Routine Description:
  10729. This function attempts to locate a nls/font related file in known
  10730. directories.
  10731. Arguments:
  10732. FileName - File name to look for.
  10733. FullPathBuffer - The full path will be put here.
  10734. FullPathBufferLength - Length of the FullPathBuffer in characters.
  10735. RequiredLength - If FullPathBuffer is too small, this is how big it
  10736. should be in characters.
  10737. Return Value:
  10738. ERROR_INSUFFICIENT_BUFFER - The FullPathBuffer is not big enough.
  10739. Win32 error code.
  10740. --*/
  10741. {
  10742. DWORD ErrorCode;
  10743. ULONG SystemRootLength;
  10744. WCHAR *System32DirName;
  10745. WCHAR *FontsDirName;
  10746. WCHAR *SystemDirName;
  10747. WCHAR *LongestDirName;
  10748. //
  10749. // Initialize locals. NOTE: The length of the longest directory
  10750. // name to concatenate to SystemRoot is used in RequiredLength
  10751. // calculation.
  10752. //
  10753. System32DirName = L"System32\\";
  10754. SystemDirName = L"System\\";
  10755. FontsDirName = L"Fonts\\";
  10756. LongestDirName = System32DirName;
  10757. //
  10758. // Get system root path.
  10759. //
  10760. SystemRootLength = ExpandEnvironmentStrings(L"%SystemRoot%\\",
  10761. FilePathBuffer,
  10762. FilePathBufferLength);
  10763. if (SystemRootLength == 0) {
  10764. ErrorCode = ERROR_BAD_FORMAT;
  10765. goto cleanup;
  10766. }
  10767. //
  10768. // SystemRootLength includes the terminating NUL. Adjust it.
  10769. //
  10770. SystemRootLength--;
  10771. //
  10772. // Calculate required length with space for terminating NUL.
  10773. //
  10774. (*RequiredLength) = SystemRootLength;
  10775. (*RequiredLength) += wcslen(LongestDirName);
  10776. (*RequiredLength) += wcslen(FileName);
  10777. (*RequiredLength) ++;
  10778. if ((*RequiredLength) > FilePathBufferLength) {
  10779. ErrorCode = ERROR_INSUFFICIENT_BUFFER;
  10780. goto cleanup;
  10781. }
  10782. //
  10783. // Look for it under system32 dir.
  10784. //
  10785. FilePathBuffer[SystemRootLength] = 0;
  10786. wcscat(FilePathBuffer, System32DirName);
  10787. wcscat(FilePathBuffer, FileName);
  10788. if (GetFileAttributes(FilePathBuffer) != INVALID_FILE_ATTRIBUTES) {
  10789. ErrorCode = ERROR_SUCCESS;
  10790. goto cleanup;
  10791. }
  10792. //
  10793. // Look for it under fonts dir.
  10794. //
  10795. FilePathBuffer[SystemRootLength] = 0;
  10796. wcscat(FilePathBuffer, FontsDirName);
  10797. wcscat(FilePathBuffer, FileName);
  10798. if (GetFileAttributes(FilePathBuffer) != INVALID_FILE_ATTRIBUTES) {
  10799. ErrorCode = ERROR_SUCCESS;
  10800. goto cleanup;
  10801. }
  10802. //
  10803. // Look for it under system dir.
  10804. //
  10805. FilePathBuffer[SystemRootLength] = 0;
  10806. wcscat(FilePathBuffer, SystemDirName);
  10807. wcscat(FilePathBuffer, FileName);
  10808. if (GetFileAttributes(FilePathBuffer) != INVALID_FILE_ATTRIBUTES) {
  10809. ErrorCode = ERROR_SUCCESS;
  10810. goto cleanup;
  10811. }
  10812. //
  10813. // Look for it at SystemRoot.
  10814. //
  10815. FilePathBuffer[SystemRootLength] = 0;
  10816. wcscat(FilePathBuffer, FileName);
  10817. if (GetFileAttributes(FilePathBuffer) != INVALID_FILE_ATTRIBUTES) {
  10818. ErrorCode = ERROR_SUCCESS;
  10819. goto cleanup;
  10820. }
  10821. //
  10822. // Could not find the file.
  10823. //
  10824. ErrorCode = ERROR_FILE_NOT_FOUND;
  10825. cleanup:
  10826. return ErrorCode;
  10827. }
  10828. DWORD
  10829. PfSvQueryNlsFileName (
  10830. HKEY Key,
  10831. WCHAR *ValueName,
  10832. WCHAR *FileNameBuffer,
  10833. ULONG FileNameBufferSize,
  10834. ULONG *RequiredSize
  10835. )
  10836. /*++
  10837. Routine Description:
  10838. This function attempts to get a file name from an NLS
  10839. CodePage/Language registry key.
  10840. Arguments:
  10841. Key - CodePage or Language key handle.
  10842. ValueName - What we are trying to get the file name for.
  10843. FileNameBuffer - Where the file name will be put.
  10844. FileNameBufferSize - Size in bytes of the file name buffer.
  10845. RequiredSize - If FileNameBuffer is too small, this is what its
  10846. size should be.
  10847. Return Value:
  10848. Win32 error code.
  10849. --*/
  10850. {
  10851. DWORD ErrorCode;
  10852. WCHAR FileValueName[MAX_PATH + 1];
  10853. ULONG BufferSize;
  10854. ULONG RegValueType;
  10855. //
  10856. // First we first get the valuename under which the file name is
  10857. // stored, then we get the file name:
  10858. //
  10859. BufferSize = MAX_PATH * sizeof(WCHAR);
  10860. ErrorCode = RegQueryValueEx(Key,
  10861. ValueName,
  10862. NULL,
  10863. &RegValueType,
  10864. (PVOID) FileValueName,
  10865. &BufferSize);
  10866. if (ErrorCode == ERROR_MORE_DATA) {
  10867. ErrorCode = ERROR_INVALID_FUNCTION;
  10868. goto cleanup;
  10869. }
  10870. if (ErrorCode != ERROR_SUCCESS) {
  10871. goto cleanup;
  10872. }
  10873. if (RegValueType != REG_SZ) {
  10874. ErrorCode = ERROR_BAD_FORMAT;
  10875. goto cleanup;
  10876. }
  10877. FileValueName[MAX_PATH - 1] = 0;
  10878. *RequiredSize = FileNameBufferSize;
  10879. ErrorCode = RegQueryValueEx(Key,
  10880. FileValueName,
  10881. NULL,
  10882. &RegValueType,
  10883. (PVOID) FileNameBuffer,
  10884. RequiredSize);
  10885. if (ErrorCode != ERROR_SUCCESS) {
  10886. goto cleanup;
  10887. }
  10888. if (RegValueType != REG_SZ) {
  10889. ErrorCode = ERROR_BAD_FORMAT;
  10890. goto cleanup;
  10891. }
  10892. if (FileNameBufferSize >= sizeof(WCHAR)) {
  10893. FileNameBuffer[(FileNameBufferSize/sizeof(WCHAR)) - 1] = 0;
  10894. }
  10895. ErrorCode = ERROR_SUCCESS;
  10896. cleanup:
  10897. return ErrorCode;
  10898. }
  10899. //
  10900. // Routines to manage / run idle tasks.
  10901. //
  10902. VOID
  10903. PfSvInitializeTask (
  10904. PPFSVC_IDLE_TASK Task
  10905. )
  10906. /*++
  10907. Routine Description:
  10908. Initialize the task structure. Should be called before any other
  10909. task functions are called. You should call the cleanup routine
  10910. on the initialized task.
  10911. Arguments:
  10912. Task - Pointer to structure.
  10913. Return Value:
  10914. None.
  10915. --*/
  10916. {
  10917. //
  10918. // Zero out the structure initializing the following to
  10919. // the right values:
  10920. //
  10921. // Registered
  10922. // WaitUnregisteredEvent
  10923. // CallbackStoppedEvent
  10924. // StartedUnregisteringEvent
  10925. // CompletedUnregisteringEvent
  10926. // Unregistering
  10927. // CallbackRunning
  10928. //
  10929. RtlZeroMemory(Task, sizeof(PFSVC_IDLE_TASK));
  10930. Task->Initialized = TRUE;
  10931. }
  10932. DWORD
  10933. PfSvRegisterTask (
  10934. PPFSVC_IDLE_TASK Task,
  10935. IT_IDLE_TASK_ID TaskId,
  10936. WAITORTIMERCALLBACK Callback,
  10937. PFSVC_IDLE_TASK_WORKER_FUNCTION DoWorkFunction
  10938. )
  10939. /*++
  10940. Routine Description:
  10941. Registers the Callback to be called when it is the turn of this
  10942. idle task to run. IFF this function returns success, you should
  10943. call unregister function before calling the cleanup function.
  10944. Arguments:
  10945. Task - Pointer to initialized task structure.
  10946. TaskId - Idle task ID to register.
  10947. Callback - We'll register a wait on the start event returned by
  10948. idle task registration with this callback. The callback should
  10949. call start/stop task callback functions appropriately.
  10950. DoWorkFunction - If the caller wants the common callback function
  10951. to be used, then this function will be called to do the actual
  10952. work in the common callback.
  10953. Return Value:
  10954. Win32 error code.
  10955. --*/
  10956. {
  10957. DWORD ErrorCode;
  10958. BOOL Success;
  10959. BOOLEAN CreatedWaitUnregisteredEvent;
  10960. BOOLEAN CreatedStartedUnregisteringEvent;
  10961. BOOLEAN CreatedCompletedUnregisteringEvent;
  10962. BOOLEAN CreatedCallbackStoppedEvent;
  10963. BOOLEAN RegisteredIdleTask;
  10964. //
  10965. // Initialize locals.
  10966. //
  10967. RegisteredIdleTask = FALSE;
  10968. CreatedWaitUnregisteredEvent = FALSE;
  10969. CreatedStartedUnregisteringEvent = FALSE;
  10970. CreatedCompletedUnregisteringEvent = FALSE;
  10971. CreatedCallbackStoppedEvent = FALSE;
  10972. DBGPR((PFID,PFTASK,"PFSVC: RegisterTask(%p,%d,%p,%p)\n",Task,TaskId,Callback,DoWorkFunction));
  10973. //
  10974. // The task should be initialized and not registered.
  10975. //
  10976. PFSVC_ASSERT(Task->Initialized);
  10977. PFSVC_ASSERT(!Task->Registered);
  10978. PFSVC_ASSERT(!Task->Unregistering);
  10979. PFSVC_ASSERT(!Task->CallbackRunning);
  10980. //
  10981. // Create the event that cleanup waits on to make sure
  10982. // the registered wait is fully unregistered.
  10983. //
  10984. Task->WaitUnregisteredEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  10985. if (Task->WaitUnregisteredEvent == NULL) {
  10986. ErrorCode = GetLastError();
  10987. goto cleanup;
  10988. }
  10989. CreatedWaitUnregisteredEvent = TRUE;
  10990. //
  10991. // Create the event that will get signaled when we start
  10992. // unregistering the task.
  10993. //
  10994. Task->StartedUnregisteringEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  10995. if (Task->StartedUnregisteringEvent == NULL) {
  10996. ErrorCode = GetLastError();
  10997. goto cleanup;
  10998. }
  10999. CreatedStartedUnregisteringEvent = TRUE;
  11000. //
  11001. // Create the event that will get signaled when we complete
  11002. // unregistering the task.
  11003. //
  11004. Task->CompletedUnregisteringEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  11005. if (Task->CompletedUnregisteringEvent == NULL) {
  11006. ErrorCode = GetLastError();
  11007. goto cleanup;
  11008. }
  11009. CreatedCompletedUnregisteringEvent = TRUE;
  11010. //
  11011. // Create the event we may wait on for the current running
  11012. // callback to go away.
  11013. //
  11014. Task->CallbackStoppedEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
  11015. if (Task->CallbackStoppedEvent == NULL) {
  11016. ErrorCode = GetLastError();
  11017. goto cleanup;
  11018. }
  11019. CreatedCallbackStoppedEvent = TRUE;
  11020. //
  11021. // Register the idle task.
  11022. //
  11023. ErrorCode = RegisterIdleTask(TaskId,
  11024. &Task->ItHandle,
  11025. &Task->StartEvent,
  11026. &Task->StopEvent);
  11027. if (ErrorCode != ERROR_SUCCESS) {
  11028. goto cleanup;
  11029. }
  11030. RegisteredIdleTask = TRUE;
  11031. //
  11032. // Register the callback: Note that once this call succeeds the task has to
  11033. // be unregistered via PfSvUnregisterTask.
  11034. //
  11035. //
  11036. // The callback might fire right away so note that we registered it and set
  11037. // up its fields upfront.
  11038. //
  11039. // FUTURE-2002/03/29-ScottMa -- For consistency, the Task->Registered
  11040. // variable should be set to TRUE & FALSE instead of 0 & 1.
  11041. Task->Registered = 1;
  11042. Task->Callback = Callback;
  11043. Task->DoWorkFunction = DoWorkFunction;
  11044. //
  11045. // If the common task callback was specified, a worker function should also
  11046. // be specified.
  11047. //
  11048. if (Callback == PfSvCommonTaskCallback) {
  11049. PFSVC_ASSERT(DoWorkFunction);
  11050. }
  11051. Success = RegisterWaitForSingleObject(&Task->WaitHandle,
  11052. Task->StartEvent,
  11053. Task->Callback,
  11054. Task,
  11055. INFINITE,
  11056. WT_EXECUTEONLYONCE | WT_EXECUTELONGFUNCTION);
  11057. if (!Success) {
  11058. //
  11059. // We failed to really register the task.
  11060. //
  11061. Task->Registered = 0;
  11062. ErrorCode = GetLastError();
  11063. goto cleanup;
  11064. }
  11065. ErrorCode = ERROR_SUCCESS;
  11066. cleanup:
  11067. DBGPR((PFID,PFTASK,"PFSVC: RegisterTask(%p)=%x\n",Task,ErrorCode));
  11068. if (ErrorCode != ERROR_SUCCESS) {
  11069. if (CreatedWaitUnregisteredEvent) {
  11070. CloseHandle(Task->WaitUnregisteredEvent);
  11071. Task->WaitUnregisteredEvent = NULL;
  11072. }
  11073. if (CreatedStartedUnregisteringEvent) {
  11074. CloseHandle(Task->StartedUnregisteringEvent);
  11075. Task->StartedUnregisteringEvent = NULL;
  11076. }
  11077. if (CreatedCompletedUnregisteringEvent) {
  11078. CloseHandle(Task->CompletedUnregisteringEvent);
  11079. Task->CompletedUnregisteringEvent = NULL;
  11080. }
  11081. if (CreatedCallbackStoppedEvent) {
  11082. CloseHandle(Task->CallbackStoppedEvent);
  11083. Task->CallbackStoppedEvent = NULL;
  11084. }
  11085. if (RegisteredIdleTask) {
  11086. UnregisterIdleTask(Task->ItHandle,
  11087. Task->StartEvent,
  11088. Task->StopEvent);
  11089. Task->StartEvent = NULL;
  11090. Task->StopEvent = NULL;
  11091. }
  11092. }
  11093. return ErrorCode;
  11094. }
  11095. DWORD
  11096. PfSvUnregisterTask (
  11097. PPFSVC_IDLE_TASK Task,
  11098. BOOLEAN CalledFromCallback
  11099. )
  11100. /*++
  11101. Routine Description:
  11102. Unregisters the idle task and the registered wait / callback. You should
  11103. call this function before calling the cleanup routine IFF the register
  11104. function returned success.
  11105. Arguments:
  11106. Task - Pointer to registered task.
  11107. CalledFromCallback - Whether this function is being called from inside
  11108. the queued callback of the task.
  11109. Return Value:
  11110. Win32 error code.
  11111. --*/
  11112. {
  11113. LONG OldValue;
  11114. LONG NewValue;
  11115. DWORD ErrorCode;
  11116. DBGPR((PFID,PFTASK,"PFSVC: UnregisterTask(%p,%d)\n",Task,(DWORD)CalledFromCallback));
  11117. //
  11118. // The task should be initialized. It may already be unregistered.
  11119. //
  11120. PFSVC_ASSERT(Task->Initialized);
  11121. if (Task->Registered == 0) {
  11122. ErrorCode = ERROR_SHUTDOWN_IN_PROGRESS;
  11123. goto cleanup;
  11124. }
  11125. //
  11126. // Distinguish whether we are unregistering the task from a callback.
  11127. //
  11128. if (CalledFromCallback) {
  11129. NewValue = PfSvcUnregisteringTaskFromCallback;
  11130. } else {
  11131. NewValue = PfSvcUnregisteringTaskFromMainThread;
  11132. }
  11133. //
  11134. // Is this task already being unregistered?
  11135. //
  11136. OldValue = InterlockedCompareExchange(&Task->Unregistering,
  11137. NewValue,
  11138. PfSvcNotUnregisteringTask);
  11139. if (OldValue != PfSvcNotUnregisteringTask) {
  11140. ErrorCode = ERROR_SHUTDOWN_IN_PROGRESS;
  11141. goto cleanup;
  11142. }
  11143. //
  11144. // *We* will be unregistering the task. There is no turning back.
  11145. //
  11146. SetEvent(Task->StartedUnregisteringEvent);
  11147. //
  11148. // If we are not inside a callback, wait for no callbacks to be running
  11149. // and cause new ones that start to bail out. We do this so we can safely
  11150. // unregister the wait.
  11151. //
  11152. if (!CalledFromCallback) {
  11153. do {
  11154. OldValue = InterlockedCompareExchange(&Task->CallbackRunning,
  11155. PfSvcTaskCallbackDisabled,
  11156. PfSvcTaskCallbackNotRunning);
  11157. if (OldValue == PfSvcTaskCallbackNotRunning) {
  11158. //
  11159. // We did it. No callbacks are running and new ones that try to
  11160. // start will bail out.
  11161. //
  11162. PFSVC_ASSERT(Task->CallbackRunning == PfSvcTaskCallbackDisabled);
  11163. break;
  11164. }
  11165. //
  11166. // A callback might be active right now. It will see that we are unregistering and
  11167. // go away. Sleep for a while and try again.
  11168. //
  11169. PFSVC_ASSERT(OldValue == PfSvcTaskCallbackRunning);
  11170. //
  11171. // We wait on this event with a timeout, because signaling of it is not
  11172. // 100% reliable because it is not under a lock etc.
  11173. //
  11174. WaitForSingleObject(Task->CallbackStoppedEvent, 1000);
  11175. } while (TRUE);
  11176. } else {
  11177. //
  11178. // We already have control of this variable as the running callback: just
  11179. // update it.
  11180. //
  11181. Task->CallbackRunning = PfSvcTaskCallbackDisabled;
  11182. }
  11183. //
  11184. // Unregister the wait. Note that in cleanup we have to wait to for
  11185. // WaitUnregisteredEvent to be signaled.
  11186. //
  11187. UnregisterWaitEx(Task->WaitHandle, Task->WaitUnregisteredEvent);
  11188. //
  11189. // Unregister the idle task.
  11190. //
  11191. UnregisterIdleTask(Task->ItHandle,
  11192. Task->StartEvent,
  11193. Task->StopEvent);
  11194. Task->StartEvent = NULL;
  11195. Task->StopEvent = NULL;
  11196. //
  11197. // Note that the task is no longer registered.
  11198. //
  11199. Task->Registered = FALSE;
  11200. SetEvent(Task->CompletedUnregisteringEvent);
  11201. ErrorCode = ERROR_SUCCESS;
  11202. cleanup:
  11203. DBGPR((PFID,PFTASK,"PFSVC: UnregisterTask(%p)=%x\n",Task,ErrorCode));
  11204. return ErrorCode;
  11205. }
  11206. VOID
  11207. PfSvCleanupTask (
  11208. PPFSVC_IDLE_TASK Task
  11209. )
  11210. /*++
  11211. Routine Description:
  11212. Cleans up all fields of an unregistered task or a task that was never
  11213. registered.
  11214. Arguments:
  11215. Task - Pointer to task.
  11216. Return Value:
  11217. None.
  11218. --*/
  11219. {
  11220. //
  11221. // The task should have been initialized.
  11222. //
  11223. PFSVC_ASSERT(Task->Initialized);
  11224. //
  11225. // If there is a WaitUnregisteredEvent, we have to wait on it
  11226. // to make sure the unregister operation is fully complete.
  11227. //
  11228. if (Task->WaitUnregisteredEvent) {
  11229. WaitForSingleObject(Task->WaitUnregisteredEvent, INFINITE);
  11230. CloseHandle(Task->WaitUnregisteredEvent);
  11231. Task->WaitUnregisteredEvent = NULL;
  11232. }
  11233. //
  11234. // If there is CompletedUnregisteringEvent, wait for it to
  11235. // be signalled to make sure both that the wait is unregistered,
  11236. // and the idle task is unregistered.
  11237. //
  11238. if (Task->CompletedUnregisteringEvent) {
  11239. WaitForSingleObject(Task->CompletedUnregisteringEvent, INFINITE);
  11240. }
  11241. //
  11242. // The task should be unregistered before it is cleaned up.
  11243. //
  11244. PFSVC_ASSERT(Task->Registered == FALSE);
  11245. //
  11246. // Cleanup task unregistering events.
  11247. //
  11248. if (Task->StartedUnregisteringEvent) {
  11249. CloseHandle(Task->StartedUnregisteringEvent);
  11250. Task->StartedUnregisteringEvent = NULL;
  11251. }
  11252. if (Task->CompletedUnregisteringEvent) {
  11253. CloseHandle(Task->CompletedUnregisteringEvent);
  11254. Task->CompletedUnregisteringEvent = NULL;
  11255. }
  11256. //
  11257. // Clean up callback stopped event.
  11258. //
  11259. if (Task->CallbackStoppedEvent) {
  11260. CloseHandle(Task->CallbackStoppedEvent);
  11261. Task->CallbackStoppedEvent = NULL;
  11262. }
  11263. Task->Initialized = FALSE;
  11264. return;
  11265. }
  11266. BOOL
  11267. PfSvStartTaskCallback(
  11268. PPFSVC_IDLE_TASK Task
  11269. )
  11270. /*++
  11271. Routine Description:
  11272. Callbacks registered via register task function should call this as the
  11273. first thing. If this function returns FALSE, the callback should go away
  11274. immediately without calling the stop-callback function.
  11275. Arguments:
  11276. Task - Pointer to task.
  11277. Return Value:
  11278. TRUE - Everything is cool.
  11279. FALSE - The task is being unregistered. Exit the callback asap.
  11280. --*/
  11281. {
  11282. BOOL ReturnValue;
  11283. LONG OldValue;
  11284. DBGPR((PFID,PFTASK,"PFSVC: StartTaskCallback(%p)\n",Task));
  11285. //
  11286. // We should not be called if the task is not initialized.
  11287. //
  11288. PFSVC_ASSERT(Task->Initialized);
  11289. do {
  11290. //
  11291. // First check if we are trying to unregister.
  11292. //
  11293. if (Task->Unregistering) {
  11294. ReturnValue = FALSE;
  11295. goto cleanup;
  11296. }
  11297. //
  11298. // Try to mark the callback running.
  11299. //
  11300. OldValue = InterlockedCompareExchange(&Task->CallbackRunning,
  11301. PfSvcTaskCallbackRunning,
  11302. PfSvcTaskCallbackNotRunning);
  11303. if (OldValue == PfSvcTaskCallbackNotRunning) {
  11304. //
  11305. // We are the running callback now. Reset the event that says
  11306. // the current callback stopped running.
  11307. //
  11308. ResetEvent(Task->CallbackStoppedEvent);
  11309. ReturnValue = TRUE;
  11310. goto cleanup;
  11311. }
  11312. //
  11313. // Either another callback is running or we are unregistering.
  11314. //
  11315. //
  11316. // Are we unregistering?
  11317. //
  11318. if (Task->Unregistering) {
  11319. ReturnValue = FALSE;
  11320. goto cleanup;
  11321. } else {
  11322. PFSVC_ASSERT(OldValue == PfSvcTaskCallbackRunning);
  11323. }
  11324. //
  11325. // Sleep for a while and try again. There should not be much conflict in this
  11326. // code, so we should hardly ever need to sleep.
  11327. //
  11328. Sleep(15);
  11329. } while (TRUE);
  11330. //
  11331. // We should not come here.
  11332. //
  11333. PFSVC_ASSERT(FALSE);
  11334. cleanup:
  11335. //
  11336. // If we are starting a callback, the task should not be in unregistered state.
  11337. //
  11338. PFSVC_ASSERT(!ReturnValue || Task->Registered);
  11339. DBGPR((PFID,PFTASK,"PFSVC: StartTaskCallback(%p)=%d\n",Task,ReturnValue));
  11340. return ReturnValue;
  11341. }
  11342. VOID
  11343. PfSvStopTaskCallback(
  11344. PPFSVC_IDLE_TASK Task
  11345. )
  11346. /*++
  11347. Routine Description:
  11348. Callbacks registered via register task function should call this as the
  11349. last thing, only if they successfully called the start callback function and
  11350. they did not unregister the task.
  11351. Arguments:
  11352. Task - Pointer to task.
  11353. Return Value:
  11354. None.
  11355. --*/
  11356. {
  11357. DBGPR((PFID,PFTASK,"PFSVC: StopTaskCallback(%p)\n",Task));
  11358. //
  11359. // The task should be registered.
  11360. //
  11361. PFSVC_ASSERT(Task->Registered);
  11362. //
  11363. // There should be a running callback.
  11364. //
  11365. PFSVC_ASSERT(Task->CallbackRunning == PfSvcTaskCallbackRunning);
  11366. Task->CallbackRunning = PfSvcTaskCallbackNotRunning;
  11367. //
  11368. // Signal the event the main thread may be waiting on to unregister
  11369. // this task.
  11370. //
  11371. SetEvent(Task->CallbackStoppedEvent);
  11372. return;
  11373. }
  11374. VOID
  11375. CALLBACK
  11376. PfSvCommonTaskCallback(
  11377. PVOID lpParameter,
  11378. BOOLEAN TimerOrWaitFired
  11379. )
  11380. /*++
  11381. Routine Description:
  11382. This is the callback for the idle tasks. It is called when the system is idle,
  11383. and it is this tasks turn to run.
  11384. Note that you cannot call PfSvCleanupTask from this thread, as it would cause
  11385. a deadlock when that function waits for registered wait callbacks to exit.
  11386. Arguments:
  11387. lpParameter - Pointer to task.
  11388. TimerOrWaitFired - Whether the callback was initiated by a timeout or the start
  11389. event getting signaled by the idle task service.
  11390. Return Value:
  11391. None.
  11392. --*/
  11393. {
  11394. HANDLE NewWaitHandle;
  11395. PPFSVC_IDLE_TASK Task;
  11396. BOOL StartedCallback;
  11397. BOOL Success;
  11398. DWORD ErrorCode;
  11399. //
  11400. // Initialize locals.
  11401. //
  11402. Task = lpParameter;
  11403. StartedCallback = FALSE;
  11404. DBGPR((PFID,PFTASK,"PFSVC: CommonTaskCallback(%p)\n",Task));
  11405. //
  11406. // Enter task callback.
  11407. //
  11408. StartedCallback = PfSvStartTaskCallback(Task);
  11409. if (!StartedCallback) {
  11410. goto cleanup;
  11411. }
  11412. //
  11413. // Do the task.
  11414. //
  11415. ErrorCode = Task->DoWorkFunction(Task);
  11416. if (ErrorCode == ERROR_RETRY) {
  11417. //
  11418. // The stop event was signaled. We will queue another callback.
  11419. //
  11420. Success = RegisterWaitForSingleObject(&NewWaitHandle,
  11421. Task->StartEvent,
  11422. Task->Callback,
  11423. Task,
  11424. INFINITE,
  11425. WT_EXECUTEONLYONCE | WT_EXECUTELONGFUNCTION);
  11426. if (Success) {
  11427. //
  11428. // Unregister the current wait handle and update it.
  11429. //
  11430. UnregisterWaitEx(Task->WaitHandle, NULL);
  11431. Task->WaitHandle = NewWaitHandle;
  11432. goto cleanup;
  11433. } else {
  11434. //
  11435. // We could not queue another callback. We will unregister,
  11436. // since we would not be able to respond to start signals
  11437. // from the idle task service. Unregister may fail only if
  11438. // the main thread is already trying to unregister.
  11439. //
  11440. ErrorCode = PfSvUnregisterTask(Task, TRUE);
  11441. if (ErrorCode == ERROR_SUCCESS) {
  11442. //
  11443. // Since *we* unregistered, we should not call stop callback.
  11444. //
  11445. StartedCallback = FALSE;
  11446. }
  11447. goto cleanup;
  11448. }
  11449. } else {
  11450. //
  11451. // The task completed. Let's unregister.
  11452. //
  11453. ErrorCode = PfSvUnregisterTask(Task, TRUE);
  11454. if (ErrorCode == ERROR_SUCCESS) {
  11455. //
  11456. // Since *we* unregistered, we should not call stop callback.
  11457. //
  11458. StartedCallback = FALSE;
  11459. }
  11460. goto cleanup;
  11461. }
  11462. //
  11463. // We should not come here.
  11464. //
  11465. PFSVC_ASSERT(FALSE);
  11466. cleanup:
  11467. DBGPR((PFID,PFTASK,"PFSVC: CommonTaskCallback(%p)=%x\n",Task,ErrorCode));
  11468. if (StartedCallback) {
  11469. PfSvStopTaskCallback(Task);
  11470. }
  11471. }
  11472. DWORD
  11473. PfSvContinueRunningTask(
  11474. PPFSVC_IDLE_TASK Task
  11475. )
  11476. /*++
  11477. Routine Description:
  11478. This is called from a running task to determine if we should continue
  11479. running this task. The task should continue running if ERROR_SUCCESS is
  11480. returned. ERROR_RETRY may be returned if the task is unregistering or
  11481. was asked to stop.
  11482. Arguments:
  11483. Task - Pointer to task. If NULL, this parameter is ignored.
  11484. Return Value:
  11485. Win32 error code.
  11486. --*/
  11487. {
  11488. DWORD WaitResult;
  11489. DWORD ErrorCode;
  11490. if (Task) {
  11491. //
  11492. // Is the task being unregistered?
  11493. //
  11494. if (Task->Unregistering) {
  11495. ErrorCode = ERROR_RETRY;
  11496. goto cleanup;
  11497. }
  11498. //
  11499. // Is the stop event signaled? We don't really wait here since
  11500. // the timeout is 0.
  11501. //
  11502. WaitResult = WaitForSingleObject(Task->StopEvent, 0);
  11503. if (WaitResult == WAIT_OBJECT_0) {
  11504. ErrorCode = ERROR_RETRY;
  11505. goto cleanup;
  11506. } else if (WaitResult != WAIT_TIMEOUT) {
  11507. //
  11508. // There was an error.
  11509. //
  11510. ErrorCode = GetLastError();
  11511. goto cleanup;
  11512. }
  11513. }
  11514. //
  11515. // Check if the service is exiting...
  11516. //
  11517. if (PfSvcGlobals.TerminateServiceEvent) {
  11518. WaitResult = WaitForSingleObject(PfSvcGlobals.TerminateServiceEvent, 0);
  11519. if (WaitResult == WAIT_OBJECT_0) {
  11520. ErrorCode = ERROR_RETRY;
  11521. goto cleanup;
  11522. } else if (WaitResult != WAIT_TIMEOUT) {
  11523. //
  11524. // There was an error.
  11525. //
  11526. ErrorCode = GetLastError();
  11527. goto cleanup;
  11528. }
  11529. }
  11530. //
  11531. // The task should continue to run.
  11532. //
  11533. ErrorCode = ERROR_SUCCESS;
  11534. cleanup:
  11535. return ErrorCode;
  11536. }
  11537. //
  11538. // ProcessIdleTasks notify routine and its dependencies.
  11539. //
  11540. VOID
  11541. PfSvProcessIdleTasksCallback(
  11542. VOID
  11543. )
  11544. /*++
  11545. Routine Description:
  11546. This is routine is registered with the idle task server as a notify
  11547. routine that is called when processing of all idle tasks is requested.
  11548. ProcessIdleTasks is usually called to prepare the system for a benchmark
  11549. run by performing the optimization tasks that would have been performed
  11550. when the system is idle.
  11551. Arguments:
  11552. None.
  11553. Return Value:
  11554. None.
  11555. --*/
  11556. {
  11557. HANDLE Events[2];
  11558. DWORD NumEvents;
  11559. DWORD WaitResult;
  11560. BOOLEAN ResetOverrideIdleEvent;
  11561. //
  11562. // First flush the idle tasks the prefetcher may have queued:
  11563. //
  11564. //
  11565. // Determine the current status of the override-idle event.
  11566. //
  11567. WaitResult = WaitForSingleObject(PfSvcGlobals.OverrideIdleProcessingEvent,
  11568. 0);
  11569. if (WaitResult != WAIT_OBJECT_0) {
  11570. //
  11571. // Override idle event is not already set. Set it and note to reset
  11572. // it once tasks are completed.
  11573. //
  11574. SetEvent(PfSvcGlobals.OverrideIdleProcessingEvent);
  11575. ResetOverrideIdleEvent = TRUE;
  11576. } else {
  11577. ResetOverrideIdleEvent = FALSE;
  11578. }
  11579. //
  11580. // Wait for processing complete event to get signaled.
  11581. //
  11582. Events[0] = PfSvcGlobals.ProcessingCompleteEvent;
  11583. Events[1] = PfSvcGlobals.TerminateServiceEvent;
  11584. NumEvents = 2;
  11585. WaitForMultipleObjects(NumEvents, Events, FALSE, 30 * 60 * 1000);
  11586. //
  11587. // If we set the override idle event, reset it.
  11588. //
  11589. if (ResetOverrideIdleEvent) {
  11590. ResetEvent(PfSvcGlobals.OverrideIdleProcessingEvent);
  11591. }
  11592. //
  11593. // Force an update of the disk layout in case it did not happen.
  11594. // If we notice no changes we will not launch the defragger again.
  11595. //
  11596. PfSvUpdateOptimalLayout(NULL);
  11597. //
  11598. // Signal WMI to complete its idle tasks if it has pending tasks.
  11599. //
  11600. PfSvForceWMIProcessIdleTasks();
  11601. return;
  11602. }
  11603. DWORD
  11604. PfSvForceWMIProcessIdleTasks(
  11605. VOID
  11606. )
  11607. /*++
  11608. Routine Description:
  11609. This is routine is called to force WMI to process all of its idle tasks.
  11610. Arguments:
  11611. None.
  11612. Return Value:
  11613. Win32 error code.
  11614. --*/
  11615. {
  11616. HANDLE StartEvent;
  11617. HANDLE DoneEvent;
  11618. HANDLE Events[2];
  11619. DWORD NumEvents;
  11620. DWORD ErrorCode;
  11621. DWORD WaitResult;
  11622. BOOL Success;
  11623. //
  11624. // Initialize locals.
  11625. //
  11626. StartEvent = NULL;
  11627. DoneEvent = NULL;
  11628. //
  11629. // Wait until WMI service is started.
  11630. //
  11631. Success = PfSvWaitForServiceToStart(L"WINMGMT", 5 * 60 * 1000);
  11632. if (!Success) {
  11633. ErrorCode = ERROR_SERVICE_NEVER_STARTED;
  11634. goto cleanup;
  11635. }
  11636. //
  11637. // Open the start and done events.
  11638. //
  11639. StartEvent = OpenEvent(EVENT_ALL_ACCESS, FALSE, L"WMI_ProcessIdleTasksStart");
  11640. DoneEvent = OpenEvent(EVENT_ALL_ACCESS, FALSE, L"WMI_ProcessIdleTasksComplete");
  11641. if (!StartEvent || !DoneEvent) {
  11642. ErrorCode = ERROR_FILE_NOT_FOUND;
  11643. goto cleanup;
  11644. }
  11645. //
  11646. // Reset the done event.
  11647. //
  11648. ResetEvent(DoneEvent);
  11649. //
  11650. // Signal the start event.
  11651. //
  11652. SetEvent(StartEvent);
  11653. //
  11654. // Wait for the done event to be signaled.
  11655. //
  11656. Events[0] = DoneEvent;
  11657. Events[1] = PfSvcGlobals.TerminateServiceEvent;
  11658. NumEvents = 2;
  11659. WaitResult = WaitForMultipleObjects(NumEvents, Events, FALSE, 25 * 60 * 1000);
  11660. switch(WaitResult) {
  11661. case WAIT_OBJECT_0 : ErrorCode = ERROR_SUCCESS; break;
  11662. case WAIT_OBJECT_0 + 1 : ErrorCode = ERROR_SHUTDOWN_IN_PROGRESS; break;
  11663. case WAIT_FAILED : ErrorCode = GetLastError(); break;
  11664. case WAIT_TIMEOUT : ErrorCode = WAIT_TIMEOUT; break;
  11665. default : ErrorCode = ERROR_INVALID_FUNCTION;
  11666. }
  11667. //
  11668. // Fall through with error code.
  11669. //
  11670. cleanup:
  11671. if (StartEvent) {
  11672. CloseHandle(StartEvent);
  11673. }
  11674. if (DoneEvent) {
  11675. CloseHandle(DoneEvent);
  11676. }
  11677. return ErrorCode;
  11678. }
  11679. BOOL
  11680. PfSvWaitForServiceToStart (
  11681. WCHAR *lpServiceName,
  11682. DWORD dwMaxWait
  11683. )
  11684. /*++
  11685. Routine Description:
  11686. Waits for the service to start.
  11687. Arguments:
  11688. lpServiceName - Service to wait for.
  11689. dwMaxWait - Timeout in ms.
  11690. Return Value:
  11691. Whether the service was started.
  11692. --*/
  11693. {
  11694. BOOL bStarted = FALSE;
  11695. DWORD dwSize = 512;
  11696. DWORD StartTickCount;
  11697. SC_HANDLE hScManager = NULL;
  11698. SC_HANDLE hService = NULL;
  11699. SERVICE_STATUS ServiceStatus;
  11700. LPQUERY_SERVICE_CONFIG lpServiceConfig = NULL;
  11701. //
  11702. // OpenSCManager and the service.
  11703. //
  11704. hScManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
  11705. if (!hScManager) {
  11706. goto Exit;
  11707. }
  11708. hService = OpenService(hScManager, lpServiceName,
  11709. SERVICE_QUERY_CONFIG | SERVICE_QUERY_STATUS);
  11710. if (!hService) {
  11711. goto Exit;
  11712. }
  11713. //
  11714. // Query if the service is going to start
  11715. //
  11716. lpServiceConfig = LocalAlloc (LPTR, dwSize);
  11717. if (!lpServiceConfig) {
  11718. goto Exit;
  11719. }
  11720. if (!QueryServiceConfig (hService, lpServiceConfig, dwSize, &dwSize)) {
  11721. if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
  11722. goto Exit;
  11723. }
  11724. LocalFree (lpServiceConfig);
  11725. lpServiceConfig = LocalAlloc (LPTR, dwSize);
  11726. if (!lpServiceConfig) {
  11727. goto Exit;
  11728. }
  11729. if (!QueryServiceConfig (hService, lpServiceConfig, dwSize, &dwSize)) {
  11730. goto Exit;
  11731. }
  11732. }
  11733. if (lpServiceConfig->dwStartType != SERVICE_AUTO_START) {
  11734. goto Exit;
  11735. }
  11736. //
  11737. // Loop until the service starts or we think it never will start
  11738. // or we've exceeded our maximum time delay.
  11739. //
  11740. StartTickCount = GetTickCount();
  11741. while (!bStarted) {
  11742. if (WAIT_OBJECT_0 == WaitForSingleObject(PfSvcGlobals.TerminateServiceEvent, 0)) {
  11743. break;
  11744. }
  11745. if ((GetTickCount() - StartTickCount) > dwMaxWait) {
  11746. break;
  11747. }
  11748. if (!QueryServiceStatus(hService, &ServiceStatus )) {
  11749. break;
  11750. }
  11751. if (ServiceStatus.dwCurrentState == SERVICE_STOPPED) {
  11752. if (ServiceStatus.dwWin32ExitCode == ERROR_SERVICE_NEVER_STARTED) {
  11753. Sleep(500);
  11754. } else {
  11755. break;
  11756. }
  11757. } else if ( (ServiceStatus.dwCurrentState == SERVICE_RUNNING) ||
  11758. (ServiceStatus.dwCurrentState == SERVICE_CONTINUE_PENDING) ||
  11759. (ServiceStatus.dwCurrentState == SERVICE_PAUSE_PENDING) ||
  11760. (ServiceStatus.dwCurrentState == SERVICE_PAUSED) ) {
  11761. bStarted = TRUE;
  11762. } else if (ServiceStatus.dwCurrentState == SERVICE_START_PENDING) {
  11763. Sleep(500);
  11764. } else {
  11765. Sleep(500);
  11766. }
  11767. }
  11768. Exit:
  11769. if (lpServiceConfig) {
  11770. LocalFree (lpServiceConfig);
  11771. }
  11772. if (hService) {
  11773. CloseServiceHandle(hService);
  11774. }
  11775. if (hScManager) {
  11776. CloseServiceHandle(hScManager);
  11777. }
  11778. return bStarted;
  11779. }
  11780. //
  11781. // Wrappers around the verify routines.
  11782. //
  11783. BOOLEAN
  11784. PfSvVerifyScenarioBuffer(
  11785. PPF_SCENARIO_HEADER Scenario,
  11786. ULONG BufferSize,
  11787. PULONG FailedCheck
  11788. )
  11789. /*++
  11790. Routine Description:
  11791. This wrapper arounding PfVerifyScenarioBuffer traps exceptions such as in-page errors that
  11792. may happen when the system is under stress. Otherwise these non-fatal failures may take
  11793. down a service-host full of important system services.
  11794. Arguments:
  11795. See PfVerifyScenarioBuffer.
  11796. Return Value:
  11797. See PfVerifyScenarioBuffer.
  11798. --*/
  11799. {
  11800. BOOLEAN Success;
  11801. __try {
  11802. Success = PfVerifyScenarioBuffer(Scenario, BufferSize, FailedCheck);
  11803. } __except (EXCEPTION_EXECUTE_HANDLER) {
  11804. //
  11805. // We should not be masking other types of exceptions.
  11806. //
  11807. PFSVC_ASSERT(GetExceptionCode() == EXCEPTION_IN_PAGE_ERROR);
  11808. Success = FALSE;
  11809. *FailedCheck = (ULONG) GetExceptionCode();
  11810. }
  11811. return Success;
  11812. }
  11813. ULONG
  11814. PfVerifyImageImportTable (
  11815. IN PVOID BaseAddress,
  11816. IN ULONG MappingSize,
  11817. IN BOOLEAN MappedAsImage
  11818. )
  11819. /*++
  11820. Routine Description:
  11821. This function tries to verify that walking the imports table of
  11822. the mapped base portion of an image file won't result in accesses beyond
  11823. the mapped region.
  11824. This includes checks to make sure DOS & NT header magics match and
  11825. they are within bounds and that strings in all import table descriptors
  11826. are NUL terminated etc.
  11827. Arguments:
  11828. BaseAddress - Pointer to base of the mapping of the image file.
  11829. MappingSize - Size in bytes of the mapping starting at BaseAddress.
  11830. MappedAsImage - Whether the image is mapping as image or as file.
  11831. Return Value:
  11832. 0 - It is safe to walk the imports of the image mapped as data.
  11833. Non 0 - It is not safe to walk the imports of the image mapped as data.
  11834. Returned value is the ID of the check failed.
  11835. --*/
  11836. {
  11837. PIMAGE_DOS_HEADER DosHeader;
  11838. PIMAGE_NT_HEADERS NtHeaders;
  11839. PIMAGE_OPTIONAL_HEADER OptionalHeader;
  11840. PIMAGE_SECTION_HEADER NtSection;
  11841. PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor;
  11842. PIMAGE_IMPORT_DESCRIPTOR FirstImportDescriptor;
  11843. PCHAR ImportedModuleName;
  11844. ULONG ImportDescriptorRVA;
  11845. ULONG ImportedModuleNameLength;
  11846. ULONG FailedCheckId;
  11847. BOOLEAN Success;
  11848. //
  11849. // Initialize locals.
  11850. //
  11851. FailedCheckId = 1;
  11852. //
  11853. // Is the mapping big enough to contain the DOS header?
  11854. //
  11855. if (MappingSize <= sizeof(IMAGE_DOS_HEADER)) {
  11856. FailedCheckId = 10;
  11857. goto cleanup;
  11858. }
  11859. DosHeader = BaseAddress;
  11860. //
  11861. // Check DOS signature and sanity check where it says NT headers are.
  11862. // The checked size is the same constant used in RtlpImageNtHeader.
  11863. //
  11864. if (DosHeader->e_magic != IMAGE_DOS_SIGNATURE ||
  11865. DosHeader->e_lfanew >= 256 * 1024 * 1024) {
  11866. FailedCheckId = 20;
  11867. goto cleanup;
  11868. }
  11869. //
  11870. // Is the NtHeaders within the bounds of mapping?
  11871. //
  11872. NtHeaders = (PIMAGE_NT_HEADERS) ((PUCHAR)BaseAddress + DosHeader->e_lfanew);
  11873. if(!PfWithinBounds(NtHeaders, BaseAddress, MappingSize)) {
  11874. FailedCheckId = 30;
  11875. goto cleanup;
  11876. }
  11877. //
  11878. // Is the full NtHeaders within the bounds of mapping?
  11879. //
  11880. if(!PfWithinBounds(NtHeaders + 1, BaseAddress, MappingSize)) {
  11881. FailedCheckId = 40;
  11882. goto cleanup;
  11883. }
  11884. //
  11885. // Check NT signature.
  11886. //
  11887. if (NtHeaders->Signature != IMAGE_NT_SIGNATURE) {
  11888. FailedCheckId = 50;
  11889. goto cleanup;
  11890. }
  11891. //
  11892. // Check the optional header size in file header.
  11893. //
  11894. if (NtHeaders->FileHeader.SizeOfOptionalHeader != IMAGE_SIZEOF_NT_OPTIONAL_HEADER) {
  11895. FailedCheckId = 60;
  11896. goto cleanup;
  11897. }
  11898. OptionalHeader = &NtHeaders->OptionalHeader;
  11899. //
  11900. // Check the optional header magic.
  11901. //
  11902. if (OptionalHeader->Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC) {
  11903. FailedCheckId = 70;
  11904. goto cleanup;
  11905. }
  11906. //
  11907. // Verify that the section headers are within the mapped region, since
  11908. // they will be used to translate RVAs to VAs.
  11909. //
  11910. NtSection = IMAGE_FIRST_SECTION(NtHeaders);
  11911. if(!PfWithinBounds(NtSection, BaseAddress, MappingSize)) {
  11912. FailedCheckId = 80;
  11913. goto cleanup;
  11914. }
  11915. if(!PfWithinBounds(NtSection + NtHeaders->FileHeader.NumberOfSections,
  11916. BaseAddress,
  11917. MappingSize)) {
  11918. FailedCheckId = 90;
  11919. goto cleanup;
  11920. }
  11921. //
  11922. // Verify that the import descriptors are within bounds.
  11923. //
  11924. ImportDescriptorRVA = OptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
  11925. if (MappedAsImage) {
  11926. ImportDescriptor = (PVOID) ((PUCHAR) BaseAddress + ImportDescriptorRVA);
  11927. } else {
  11928. ImportDescriptor = ImageRvaToVa(NtHeaders, BaseAddress, ImportDescriptorRVA, NULL);
  11929. }
  11930. FirstImportDescriptor = ImportDescriptor;
  11931. //
  11932. // Make sure that the first import descriptor is fully within bounds.
  11933. //
  11934. if (!PfWithinBounds(ImportDescriptor, BaseAddress, MappingSize)) {
  11935. FailedCheckId = 100;
  11936. goto cleanup;
  11937. }
  11938. if (!PfWithinBounds(((PCHAR)(ImportDescriptor + 1)) - 1, BaseAddress, MappingSize)) {
  11939. FailedCheckId = 100;
  11940. goto cleanup;
  11941. }
  11942. while (ImportDescriptor->Name && ImportDescriptor->FirstThunk) {
  11943. //
  11944. // Is the first thunk within bounds?
  11945. //
  11946. if (!PfWithinBounds((PUCHAR)BaseAddress + ImportDescriptor->FirstThunk,
  11947. BaseAddress,
  11948. MappingSize)) {
  11949. FailedCheckId = 110;
  11950. goto cleanup;
  11951. }
  11952. //
  11953. // Is the Name within bounds?
  11954. //
  11955. if (MappedAsImage) {
  11956. ImportedModuleName = (PCHAR) BaseAddress + ImportDescriptor->Name;
  11957. } else {
  11958. ImportedModuleName = ImageRvaToVa(NtHeaders, BaseAddress, ImportDescriptor->Name, NULL);
  11959. }
  11960. //
  11961. // Is the first character within bounds?
  11962. //
  11963. if (!PfWithinBounds(ImportedModuleName, BaseAddress, MappingSize)) {
  11964. FailedCheckId = 120;
  11965. goto cleanup;
  11966. }
  11967. ImportedModuleNameLength = 0;
  11968. while (*ImportedModuleName) {
  11969. ImportedModuleName++;
  11970. ImportedModuleNameLength++;
  11971. //
  11972. // If the import name is too long bail out.
  11973. //
  11974. if (ImportedModuleNameLength >= MAX_PATH) {
  11975. FailedCheckId = 130;
  11976. goto cleanup;
  11977. }
  11978. //
  11979. // Check the next character before accessing it.
  11980. //
  11981. if (!PfWithinBounds(ImportedModuleName, BaseAddress, MappingSize)) {
  11982. FailedCheckId = 140;
  11983. goto cleanup;
  11984. }
  11985. }
  11986. ImportDescriptor++;
  11987. //
  11988. // Make sure that the next import descriptor is fully within bounds
  11989. // before we access its fields in the while loop condition check.
  11990. //
  11991. if (!PfWithinBounds(((PCHAR)(ImportDescriptor + 1)) - 1, BaseAddress, MappingSize)) {
  11992. FailedCheckId = 150;
  11993. goto cleanup;
  11994. }
  11995. }
  11996. //
  11997. // We passed all the checks.
  11998. //
  11999. FailedCheckId = 0;
  12000. cleanup:
  12001. return FailedCheckId;
  12002. }
  12003. //
  12004. // Try to keep the verification code below at the end of the file so it is
  12005. // easier to copy.
  12006. //
  12007. //
  12008. // Verification code shared between the kernel and user mode
  12009. // components. This code should be kept in sync with a simple copy &
  12010. // paste, so don't add any kernel/user specific code/macros. Note that
  12011. // the prefix on the function names are Pf, just like it is with
  12012. // shared structures / constants.
  12013. //
  12014. BOOLEAN
  12015. __forceinline
  12016. PfWithinBounds(
  12017. PVOID Pointer,
  12018. PVOID Base,
  12019. ULONG Length
  12020. )
  12021. /*++
  12022. Routine Description:
  12023. Check whether the pointer is within Length bytes from the base.
  12024. Arguments:
  12025. Pointer - Pointer to check.
  12026. Base - Pointer to base of mapping/array etc.
  12027. Length - Number of bytes that are valid starting from Base.
  12028. Return Value:
  12029. TRUE - Pointer is within bounds.
  12030. FALSE - Pointer is not within bounds.
  12031. --*/
  12032. {
  12033. if (((PCHAR)Pointer < (PCHAR)Base) ||
  12034. ((PCHAR)Pointer >= ((PCHAR)Base + Length))) {
  12035. return FALSE;
  12036. } else {
  12037. return TRUE;
  12038. }
  12039. }
  12040. BOOLEAN
  12041. PfVerifyScenarioId (
  12042. PPF_SCENARIO_ID ScenarioId
  12043. )
  12044. /*++
  12045. Routine Description:
  12046. Verify that the scenario id is sensible.
  12047. Arguments:
  12048. ScenarioId - Scenario Id to verify.
  12049. Return Value:
  12050. TRUE - ScenarioId is fine.
  12051. FALSE - ScenarioId is corrupt.
  12052. --*/
  12053. {
  12054. LONG CurCharIdx;
  12055. //
  12056. // Make sure the scenario name is NUL terminated.
  12057. //
  12058. for (CurCharIdx = PF_SCEN_ID_MAX_CHARS; CurCharIdx >= 0; CurCharIdx--) {
  12059. if (ScenarioId->ScenName[CurCharIdx] == 0) {
  12060. break;
  12061. }
  12062. }
  12063. if (ScenarioId->ScenName[CurCharIdx] != 0) {
  12064. return FALSE;
  12065. }
  12066. //
  12067. // Make sure there is a scenario name.
  12068. //
  12069. if (CurCharIdx == 0) {
  12070. return FALSE;
  12071. }
  12072. //
  12073. // Checks passed.
  12074. //
  12075. return TRUE;
  12076. }
  12077. // ISSUE-2002/03/29-ScottMa -- FilePaths and VolumePaths must be upcased in
  12078. // the scenario file, or subsequent comparisons will fail. Verify the case
  12079. // of the embedded names in this function.
  12080. BOOLEAN
  12081. PfVerifyScenarioBuffer(
  12082. PPF_SCENARIO_HEADER Scenario,
  12083. ULONG BufferSize,
  12084. PULONG FailedCheck
  12085. )
  12086. /*++
  12087. Routine Description:
  12088. Verify offset and indices in a scenario file are not beyond
  12089. bounds. This code is shared between the user mode service and
  12090. kernel mode component. If you update this function, update it in
  12091. both.
  12092. Arguments:
  12093. Scenario - Base of mapped view of the whole file.
  12094. BufferSize - Size of the scenario buffer.
  12095. FailedCheck - If verify failed, Id for the check that was failed.
  12096. Return Value:
  12097. TRUE - Scenario is fine.
  12098. FALSE - Scenario is corrupt.
  12099. --*/
  12100. {
  12101. PPF_SECTION_RECORD Sections;
  12102. PPF_SECTION_RECORD pSection;
  12103. ULONG SectionIdx;
  12104. PPF_PAGE_RECORD Pages;
  12105. PPF_PAGE_RECORD pPage;
  12106. LONG PageIdx;
  12107. PCHAR FileNames;
  12108. PCHAR pFileNameStart;
  12109. PCHAR pFileNameEnd;
  12110. PWCHAR pwFileName;
  12111. LONG FailedCheckId;
  12112. ULONG NumRemainingPages;
  12113. ULONG NumPages;
  12114. LONG PreviousPageIdx;
  12115. ULONG FileNameSize;
  12116. BOOLEAN ScenarioVerified;
  12117. PCHAR MetadataInfoBase;
  12118. PPF_METADATA_RECORD MetadataRecordTable;
  12119. PPF_METADATA_RECORD MetadataRecord;
  12120. ULONG MetadataRecordIdx;
  12121. PWCHAR VolumePath;
  12122. PFILE_PREFETCH FilePrefetchInfo;
  12123. ULONG FilePrefetchInfoSize;
  12124. PPF_COUNTED_STRING DirectoryPath;
  12125. ULONG DirectoryIdx;
  12126. //
  12127. // Initialize locals.
  12128. //
  12129. FailedCheckId = 0;
  12130. //
  12131. // Initialize return value to FALSE. It will be set to TRUE only
  12132. // after all the checks pass.
  12133. //
  12134. ScenarioVerified = FALSE;
  12135. //
  12136. // The buffer should at least contain the scenario header.
  12137. //
  12138. if (BufferSize < sizeof(PF_SCENARIO_HEADER)) {
  12139. FailedCheckId = 10;
  12140. goto cleanup;
  12141. }
  12142. if ((ULONG_PTR)Scenario & (_alignof(PF_SCENARIO_HEADER) - 1)) {
  12143. FailedCheckId = 15;
  12144. goto cleanup;
  12145. }
  12146. //
  12147. // Check version and magic on the header.
  12148. //
  12149. if (Scenario->Version != PF_CURRENT_VERSION ||
  12150. Scenario->MagicNumber != PF_SCENARIO_MAGIC_NUMBER) {
  12151. FailedCheckId = 20;
  12152. goto cleanup;
  12153. }
  12154. //
  12155. // The buffer should not be greater than max allowed size.
  12156. //
  12157. if (BufferSize > PF_MAXIMUM_SCENARIO_SIZE) {
  12158. FailedCheckId = 25;
  12159. goto cleanup;
  12160. }
  12161. if (BufferSize != Scenario->Size) {
  12162. FailedCheckId = 26;
  12163. goto cleanup;
  12164. }
  12165. //
  12166. // Check for legal scenario type.
  12167. //
  12168. if (Scenario->ScenarioType < 0 || Scenario->ScenarioType >= PfMaxScenarioType) {
  12169. FailedCheckId = 27;
  12170. goto cleanup;
  12171. }
  12172. //
  12173. // Check limits on number of pages, sections etc.
  12174. //
  12175. if (Scenario->NumSections > PF_MAXIMUM_SECTIONS ||
  12176. Scenario->NumMetadataRecords > PF_MAXIMUM_SECTIONS ||
  12177. Scenario->NumPages > PF_MAXIMUM_PAGES ||
  12178. Scenario->FileNameInfoSize > PF_MAXIMUM_FILE_NAME_DATA_SIZE) {
  12179. FailedCheckId = 30;
  12180. goto cleanup;
  12181. }
  12182. if (Scenario->NumSections == 0 ||
  12183. Scenario->NumPages == 0 ||
  12184. Scenario->FileNameInfoSize == 0) {
  12185. FailedCheckId = 33;
  12186. goto cleanup;
  12187. }
  12188. //
  12189. // Check limit on sensitivity.
  12190. //
  12191. if (Scenario->Sensitivity < PF_MIN_SENSITIVITY ||
  12192. Scenario->Sensitivity > PF_MAX_SENSITIVITY) {
  12193. FailedCheckId = 35;
  12194. goto cleanup;
  12195. }
  12196. //
  12197. // Make sure the scenario id is valid.
  12198. //
  12199. if (!PfVerifyScenarioId(&Scenario->ScenarioId)) {
  12200. FailedCheckId = 37;
  12201. goto cleanup;
  12202. }
  12203. //
  12204. // Initialize pointers to tables.
  12205. //
  12206. Sections = (PPF_SECTION_RECORD) ((PCHAR)Scenario + Scenario->SectionInfoOffset);
  12207. if ((ULONG_PTR)Sections & (_alignof(PF_SECTION_RECORD) - 1)) {
  12208. FailedCheckId = 38;
  12209. goto cleanup;
  12210. }
  12211. if (!PfWithinBounds(Sections, Scenario, BufferSize)) {
  12212. FailedCheckId = 40;
  12213. goto cleanup;
  12214. }
  12215. if (!PfWithinBounds((PCHAR) &Sections[Scenario->NumSections] - 1,
  12216. Scenario,
  12217. BufferSize)) {
  12218. FailedCheckId = 45;
  12219. goto cleanup;
  12220. }
  12221. Pages = (PPF_PAGE_RECORD) ((PCHAR)Scenario + Scenario->PageInfoOffset);
  12222. if ((ULONG_PTR)Pages & (_alignof(PF_PAGE_RECORD) - 1)) {
  12223. FailedCheckId = 47;
  12224. goto cleanup;
  12225. }
  12226. if (!PfWithinBounds(Pages, Scenario, BufferSize)) {
  12227. FailedCheckId = 50;
  12228. goto cleanup;
  12229. }
  12230. if (!PfWithinBounds((PCHAR) &Pages[Scenario->NumPages] - 1,
  12231. Scenario,
  12232. BufferSize)) {
  12233. FailedCheckId = 55;
  12234. goto cleanup;
  12235. }
  12236. FileNames = (PCHAR)Scenario + Scenario->FileNameInfoOffset;
  12237. if ((ULONG_PTR)FileNames & (_alignof(WCHAR) - 1)) {
  12238. FailedCheckId = 57;
  12239. goto cleanup;
  12240. }
  12241. if (!PfWithinBounds(FileNames, Scenario, BufferSize)) {
  12242. FailedCheckId = 60;
  12243. goto cleanup;
  12244. }
  12245. if (!PfWithinBounds(FileNames + Scenario->FileNameInfoSize - 1,
  12246. Scenario,
  12247. BufferSize)) {
  12248. FailedCheckId = 70;
  12249. goto cleanup;
  12250. }
  12251. MetadataInfoBase = (PCHAR)Scenario + Scenario->MetadataInfoOffset;
  12252. MetadataRecordTable = (PPF_METADATA_RECORD) MetadataInfoBase;
  12253. if ((ULONG_PTR)MetadataRecordTable & (_alignof(PF_METADATA_RECORD) - 1)) {
  12254. FailedCheckId = 72;
  12255. goto cleanup;
  12256. }
  12257. if (!PfWithinBounds(MetadataInfoBase, Scenario, BufferSize)) {
  12258. FailedCheckId = 73;
  12259. goto cleanup;
  12260. }
  12261. if (!PfWithinBounds(MetadataInfoBase + Scenario->MetadataInfoSize - 1,
  12262. Scenario,
  12263. BufferSize)) {
  12264. FailedCheckId = 74;
  12265. goto cleanup;
  12266. }
  12267. if (!PfWithinBounds(((PCHAR) &MetadataRecordTable[Scenario->NumMetadataRecords]) - 1,
  12268. Scenario,
  12269. BufferSize)) {
  12270. FailedCheckId = 75;
  12271. goto cleanup;
  12272. }
  12273. //
  12274. // Verify that sections contain valid information.
  12275. //
  12276. NumRemainingPages = Scenario->NumPages;
  12277. for (SectionIdx = 0; SectionIdx < Scenario->NumSections; SectionIdx++) {
  12278. pSection = &Sections[SectionIdx];
  12279. //
  12280. // Check if file name is within bounds.
  12281. //
  12282. pFileNameStart = FileNames + pSection->FileNameOffset;
  12283. if ((ULONG_PTR)pFileNameStart & (_alignof(WCHAR) - 1)) {
  12284. FailedCheckId = 77;
  12285. goto cleanup;
  12286. }
  12287. if (!PfWithinBounds(pFileNameStart, Scenario, BufferSize)) {
  12288. FailedCheckId = 80;
  12289. goto cleanup;
  12290. }
  12291. //
  12292. // Make sure there is a valid sized file name.
  12293. //
  12294. if (pSection->FileNameLength == 0) {
  12295. FailedCheckId = 90;
  12296. goto cleanup;
  12297. }
  12298. //
  12299. // Check file name max length.
  12300. //
  12301. if (pSection->FileNameLength > PF_MAXIMUM_SECTION_FILE_NAME_LENGTH) {
  12302. FailedCheckId = 100;
  12303. goto cleanup;
  12304. }
  12305. //
  12306. // Note that pFileNameEnd gets a -1 so it is the address of
  12307. // the last byte.
  12308. //
  12309. FileNameSize = (pSection->FileNameLength + 1) * sizeof(WCHAR);
  12310. pFileNameEnd = pFileNameStart + FileNameSize - 1;
  12311. if (!PfWithinBounds(pFileNameEnd, Scenario, BufferSize)) {
  12312. FailedCheckId = 110;
  12313. goto cleanup;
  12314. }
  12315. //
  12316. // Check if the file name is NUL terminated.
  12317. //
  12318. pwFileName = (PWCHAR) pFileNameStart;
  12319. if (pwFileName[pSection->FileNameLength] != 0) {
  12320. FailedCheckId = 120;
  12321. goto cleanup;
  12322. }
  12323. //
  12324. // Check max number of pages in a section.
  12325. //
  12326. if (pSection->NumPages > PF_MAXIMUM_SECTION_PAGES) {
  12327. FailedCheckId = 140;
  12328. goto cleanup;
  12329. }
  12330. //
  12331. // Make sure NumPages for the section is at least less
  12332. // than the remaining pages in the scenario. Then update the
  12333. // remaining pages.
  12334. //
  12335. if (pSection->NumPages > NumRemainingPages) {
  12336. FailedCheckId = 150;
  12337. goto cleanup;
  12338. }
  12339. NumRemainingPages -= pSection->NumPages;
  12340. //
  12341. // Verify that there are NumPages pages in our page list and
  12342. // they are sorted by file offset.
  12343. //
  12344. PageIdx = pSection->FirstPageIdx;
  12345. NumPages = 0;
  12346. PreviousPageIdx = PF_INVALID_PAGE_IDX;
  12347. while (PageIdx != PF_INVALID_PAGE_IDX) {
  12348. //
  12349. // Check that page idx is within range.
  12350. //
  12351. if (PageIdx < 0 || (ULONG) PageIdx >= Scenario->NumPages) {
  12352. FailedCheckId = 160;
  12353. goto cleanup;
  12354. }
  12355. //
  12356. // If this is not the first page record, make sure it
  12357. // comes after the previous one. We also check for
  12358. // duplicate offset here.
  12359. //
  12360. if (PreviousPageIdx != PF_INVALID_PAGE_IDX) {
  12361. if (Pages[PageIdx].FileOffset <=
  12362. Pages[PreviousPageIdx].FileOffset) {
  12363. FailedCheckId = 165;
  12364. goto cleanup;
  12365. }
  12366. }
  12367. //
  12368. // Update the last page index.
  12369. //
  12370. PreviousPageIdx = PageIdx;
  12371. //
  12372. // Get the next page index.
  12373. //
  12374. pPage = &Pages[PageIdx];
  12375. PageIdx = pPage->NextPageIdx;
  12376. //
  12377. // Update the number of pages we've seen on the list so
  12378. // far. If it is greater than what there should be on the
  12379. // list we have a problem. We may have even hit a list.
  12380. //
  12381. NumPages++;
  12382. if (NumPages > pSection->NumPages) {
  12383. FailedCheckId = 170;
  12384. goto cleanup;
  12385. }
  12386. }
  12387. //
  12388. // Make sure the section has exactly the number of pages it
  12389. // says it does.
  12390. //
  12391. if (NumPages != pSection->NumPages) {
  12392. FailedCheckId = 180;
  12393. goto cleanup;
  12394. }
  12395. }
  12396. //
  12397. // We should have accounted for all pages in the scenario.
  12398. //
  12399. if (NumRemainingPages) {
  12400. FailedCheckId = 190;
  12401. goto cleanup;
  12402. }
  12403. //
  12404. // Make sure metadata prefetch records make sense.
  12405. //
  12406. for (MetadataRecordIdx = 0;
  12407. MetadataRecordIdx < Scenario->NumMetadataRecords;
  12408. MetadataRecordIdx++) {
  12409. MetadataRecord = &MetadataRecordTable[MetadataRecordIdx];
  12410. //
  12411. // Make sure that the volume path is within bounds and NUL
  12412. // terminated.
  12413. //
  12414. VolumePath = (PWCHAR)(MetadataInfoBase + MetadataRecord->VolumeNameOffset);
  12415. if ((ULONG_PTR)VolumePath & (_alignof(WCHAR) - 1)) {
  12416. FailedCheckId = 195;
  12417. goto cleanup;
  12418. }
  12419. if (!PfWithinBounds(VolumePath, Scenario, BufferSize)) {
  12420. FailedCheckId = 200;
  12421. goto cleanup;
  12422. }
  12423. if (!PfWithinBounds(((PCHAR)(VolumePath + MetadataRecord->VolumeNameLength + 1)) - 1,
  12424. Scenario,
  12425. BufferSize)) {
  12426. FailedCheckId = 210;
  12427. goto cleanup;
  12428. }
  12429. if (VolumePath[MetadataRecord->VolumeNameLength] != 0) {
  12430. FailedCheckId = 220;
  12431. goto cleanup;
  12432. }
  12433. //
  12434. // Make sure that FilePrefetchInformation is within bounds.
  12435. //
  12436. FilePrefetchInfo = (PFILE_PREFETCH)
  12437. (MetadataInfoBase + MetadataRecord->FilePrefetchInfoOffset);
  12438. if ((ULONG_PTR)FilePrefetchInfo & (_alignof(FILE_PREFETCH) - 1)) {
  12439. FailedCheckId = 225;
  12440. goto cleanup;
  12441. }
  12442. if (!PfWithinBounds(FilePrefetchInfo, Scenario, BufferSize)) {
  12443. FailedCheckId = 230;
  12444. goto cleanup;
  12445. }
  12446. //
  12447. // Its size should be greater than size of a FILE_PREFETCH
  12448. // structure (so we can safely access the fields).
  12449. //
  12450. if (MetadataRecord->FilePrefetchInfoSize < sizeof(FILE_PREFETCH)) {
  12451. FailedCheckId = 240;
  12452. goto cleanup;
  12453. }
  12454. if (!PfWithinBounds((PCHAR)FilePrefetchInfo + MetadataRecord->FilePrefetchInfoSize - 1,
  12455. Scenario,
  12456. BufferSize)) {
  12457. FailedCheckId = 245;
  12458. goto cleanup;
  12459. }
  12460. //
  12461. // It should be for prefetching file creates.
  12462. //
  12463. if (FilePrefetchInfo->Type != FILE_PREFETCH_TYPE_FOR_CREATE) {
  12464. FailedCheckId = 250;
  12465. goto cleanup;
  12466. }
  12467. //
  12468. // There should not be more entries then are files and
  12469. // directories. The number of inidividual directories may be
  12470. // more than what we allow for, but it would be highly rare to
  12471. // be suspicious and thus ignored.
  12472. //
  12473. if (FilePrefetchInfo->Count > PF_MAXIMUM_DIRECTORIES + PF_MAXIMUM_SECTIONS) {
  12474. FailedCheckId = 260;
  12475. goto cleanup;
  12476. }
  12477. //
  12478. // Its size should match the size calculated by number of file
  12479. // index numbers specified in the header.
  12480. //
  12481. FilePrefetchInfoSize = sizeof(FILE_PREFETCH);
  12482. if (FilePrefetchInfo->Count) {
  12483. FilePrefetchInfoSize += (FilePrefetchInfo->Count - 1) * sizeof(ULONGLONG);
  12484. }
  12485. if (FilePrefetchInfoSize != MetadataRecord->FilePrefetchInfoSize) {
  12486. FailedCheckId = 270;
  12487. goto cleanup;
  12488. }
  12489. //
  12490. // Make sure that the directory paths for this volume make
  12491. // sense.
  12492. //
  12493. if (MetadataRecord->NumDirectories > PF_MAXIMUM_DIRECTORIES) {
  12494. FailedCheckId = 280;
  12495. goto cleanup;
  12496. }
  12497. DirectoryPath = (PPF_COUNTED_STRING)
  12498. (MetadataInfoBase + MetadataRecord->DirectoryPathsOffset);
  12499. if ((ULONG_PTR)DirectoryPath & (_alignof(PF_COUNTED_STRING) - 1)) {
  12500. FailedCheckId = 283;
  12501. goto cleanup;
  12502. }
  12503. for (DirectoryIdx = 0;
  12504. DirectoryIdx < MetadataRecord->NumDirectories;
  12505. DirectoryIdx ++) {
  12506. //
  12507. // Make sure head of the structure is within bounds.
  12508. //
  12509. if (!PfWithinBounds(DirectoryPath, Scenario, BufferSize)) {
  12510. FailedCheckId = 285;
  12511. goto cleanup;
  12512. }
  12513. if (!PfWithinBounds((PCHAR)DirectoryPath + sizeof(PF_COUNTED_STRING) - 1,
  12514. Scenario,
  12515. BufferSize)) {
  12516. FailedCheckId = 290;
  12517. goto cleanup;
  12518. }
  12519. //
  12520. // Check the length of the string.
  12521. //
  12522. if (DirectoryPath->Length >= PF_MAXIMUM_SECTION_FILE_NAME_LENGTH) {
  12523. FailedCheckId = 300;
  12524. goto cleanup;
  12525. }
  12526. //
  12527. // Make sure end of the string is within bounds.
  12528. //
  12529. if (!PfWithinBounds((PCHAR)(&DirectoryPath->String[DirectoryPath->Length + 1]) - 1,
  12530. Scenario,
  12531. BufferSize)) {
  12532. FailedCheckId = 310;
  12533. goto cleanup;
  12534. }
  12535. //
  12536. // Make sure the string is NUL terminated.
  12537. //
  12538. if (DirectoryPath->String[DirectoryPath->Length] != 0) {
  12539. FailedCheckId = 320;
  12540. goto cleanup;
  12541. }
  12542. //
  12543. // Set pointer to next DirectoryPath.
  12544. //
  12545. DirectoryPath = (PPF_COUNTED_STRING)
  12546. (&DirectoryPath->String[DirectoryPath->Length + 1]);
  12547. }
  12548. }
  12549. //
  12550. // We've passed all the checks.
  12551. //
  12552. ScenarioVerified = TRUE;
  12553. cleanup:
  12554. *FailedCheck = FailedCheckId;
  12555. return ScenarioVerified;
  12556. }
  12557. BOOLEAN
  12558. PfVerifyTraceBuffer(
  12559. PPF_TRACE_HEADER Trace,
  12560. ULONG BufferSize,
  12561. PULONG FailedCheck
  12562. )
  12563. /*++
  12564. Routine Description:
  12565. Verify offset and indices in a trace buffer are not beyond
  12566. bounds. This code is shared between the user mode service and
  12567. kernel mode component. If you update this function, update it in
  12568. both.
  12569. Arguments:
  12570. Trace - Base of Trace buffer.
  12571. BufferSize - Size of the scenario file / mapping.
  12572. FailedCheck - If verify failed, Id for the check that was failed.
  12573. Return Value:
  12574. TRUE - Trace is fine.
  12575. FALSE - Trace is corrupt;
  12576. --*/
  12577. {
  12578. LONG FailedCheckId;
  12579. PPF_LOG_ENTRY LogEntries;
  12580. PPF_SECTION_INFO Section;
  12581. PPF_VOLUME_INFO VolumeInfo;
  12582. ULONG SectionLength;
  12583. ULONG EntryIdx;
  12584. ULONG SectionIdx;
  12585. ULONG TotalFaults;
  12586. ULONG PeriodIdx;
  12587. ULONG VolumeIdx;
  12588. BOOLEAN TraceVerified;
  12589. ULONG VolumeInfoSize;
  12590. //
  12591. // Initialize locals:
  12592. //
  12593. FailedCheckId = 0;
  12594. //
  12595. // Initialize return value to FALSE. It will be set to TRUE only
  12596. // after all the checks pass.
  12597. //
  12598. TraceVerified = FALSE;
  12599. //
  12600. // The buffer should at least contain the scenario header.
  12601. //
  12602. if (BufferSize < sizeof(PF_TRACE_HEADER)) {
  12603. FailedCheckId = 10;
  12604. goto cleanup;
  12605. }
  12606. //
  12607. // Check trace header alignment.
  12608. //
  12609. if ((ULONG_PTR)Trace & (_alignof(PF_TRACE_HEADER) - 1)) {
  12610. FailedCheckId = 15;
  12611. goto cleanup;
  12612. }
  12613. //
  12614. // Check version and magic on the header.
  12615. //
  12616. if (Trace->Version != PF_CURRENT_VERSION ||
  12617. Trace->MagicNumber != PF_TRACE_MAGIC_NUMBER) {
  12618. FailedCheckId = 20;
  12619. goto cleanup;
  12620. }
  12621. //
  12622. // The buffer should not be greater than max allowed size.
  12623. //
  12624. if (BufferSize > PF_MAXIMUM_TRACE_SIZE) {
  12625. FailedCheckId = 23;
  12626. goto cleanup;
  12627. }
  12628. //
  12629. // Check for legal scenario type.
  12630. //
  12631. if (Trace->ScenarioType < 0 || Trace->ScenarioType >= PfMaxScenarioType) {
  12632. FailedCheckId = 25;
  12633. goto cleanup;
  12634. }
  12635. //
  12636. // Check limits on number of pages, sections etc.
  12637. //
  12638. if (Trace->NumSections > PF_MAXIMUM_SECTIONS ||
  12639. Trace->NumEntries > PF_MAXIMUM_LOG_ENTRIES ||
  12640. Trace->NumVolumes > PF_MAXIMUM_SECTIONS) {
  12641. FailedCheckId = 30;
  12642. goto cleanup;
  12643. }
  12644. //
  12645. // Check buffer size and the size of the trace.
  12646. //
  12647. if (Trace->Size != BufferSize) {
  12648. FailedCheckId = 35;
  12649. goto cleanup;
  12650. }
  12651. //
  12652. // Make sure the scenario id is valid.
  12653. //
  12654. if (!PfVerifyScenarioId(&Trace->ScenarioId)) {
  12655. FailedCheckId = 37;
  12656. goto cleanup;
  12657. }
  12658. //
  12659. // Check Bounds of Trace Buffer
  12660. //
  12661. LogEntries = (PPF_LOG_ENTRY) ((PCHAR)Trace + Trace->TraceBufferOffset);
  12662. if ((ULONG_PTR)LogEntries & (_alignof(PF_LOG_ENTRY) - 1)) {
  12663. FailedCheckId = 38;
  12664. goto cleanup;
  12665. }
  12666. if (!PfWithinBounds(LogEntries, Trace, BufferSize)) {
  12667. FailedCheckId = 40;
  12668. goto cleanup;
  12669. }
  12670. if (!PfWithinBounds((PCHAR)&LogEntries[Trace->NumEntries] - 1,
  12671. Trace,
  12672. BufferSize)) {
  12673. FailedCheckId = 50;
  12674. goto cleanup;
  12675. }
  12676. //
  12677. // Verify pages contain valid information.
  12678. //
  12679. for (EntryIdx = 0; EntryIdx < Trace->NumEntries; EntryIdx++) {
  12680. //
  12681. // Make sure sequence number is within bounds.
  12682. //
  12683. if (LogEntries[EntryIdx].SectionId >= Trace->NumSections) {
  12684. FailedCheckId = 60;
  12685. goto cleanup;
  12686. }
  12687. }
  12688. //
  12689. // Verify section info entries are valid.
  12690. //
  12691. Section = (PPF_SECTION_INFO) ((PCHAR)Trace + Trace->SectionInfoOffset);
  12692. if ((ULONG_PTR)Section & (_alignof(PF_SECTION_INFO) - 1)) {
  12693. FailedCheckId = 65;
  12694. goto cleanup;
  12695. }
  12696. for (SectionIdx = 0; SectionIdx < Trace->NumSections; SectionIdx++) {
  12697. //
  12698. // Make sure the section is within bounds.
  12699. //
  12700. if (!PfWithinBounds(Section, Trace, BufferSize)) {
  12701. FailedCheckId = 70;
  12702. goto cleanup;
  12703. }
  12704. if (!PfWithinBounds((PCHAR)Section + sizeof(PF_SECTION_INFO) - 1,
  12705. Trace,
  12706. BufferSize)) {
  12707. FailedCheckId = 75;
  12708. goto cleanup;
  12709. }
  12710. //
  12711. // Make sure the file name is not too big.
  12712. //
  12713. if(Section->FileNameLength > PF_MAXIMUM_SECTION_FILE_NAME_LENGTH) {
  12714. FailedCheckId = 80;
  12715. goto cleanup;
  12716. }
  12717. //
  12718. // Calculate size of this section entry.
  12719. //
  12720. SectionLength = sizeof(PF_SECTION_INFO) +
  12721. (Section->FileNameLength) * sizeof(WCHAR);
  12722. //
  12723. // Make sure all of the data in the section info is within
  12724. // bounds.
  12725. //
  12726. if (!PfWithinBounds((PUCHAR)Section + SectionLength - 1,
  12727. Trace,
  12728. BufferSize)) {
  12729. FailedCheckId = 90;
  12730. goto cleanup;
  12731. }
  12732. //
  12733. // Make sure the file name is NUL terminated.
  12734. //
  12735. if (Section->FileName[Section->FileNameLength] != 0) {
  12736. FailedCheckId = 100;
  12737. goto cleanup;
  12738. }
  12739. //
  12740. // Set pointer to next section.
  12741. //
  12742. Section = (PPF_SECTION_INFO) ((PUCHAR) Section + SectionLength);
  12743. }
  12744. //
  12745. // Check FaultsPerPeriod information.
  12746. //
  12747. TotalFaults = 0;
  12748. for (PeriodIdx = 0; PeriodIdx < PF_MAX_NUM_TRACE_PERIODS; PeriodIdx++) {
  12749. TotalFaults += Trace->FaultsPerPeriod[PeriodIdx];
  12750. }
  12751. if (TotalFaults != Trace->NumEntries) {
  12752. FailedCheckId = 120;
  12753. goto cleanup;
  12754. }
  12755. //
  12756. // Verify the volume information block.
  12757. //
  12758. VolumeInfo = (PPF_VOLUME_INFO) ((PCHAR)Trace + Trace->VolumeInfoOffset);
  12759. if ((ULONG_PTR)VolumeInfo & (_alignof(PF_VOLUME_INFO) - 1)) {
  12760. FailedCheckId = 125;
  12761. goto cleanup;
  12762. }
  12763. if (!PfWithinBounds(VolumeInfo, Trace, BufferSize)) {
  12764. FailedCheckId = 130;
  12765. goto cleanup;
  12766. }
  12767. if (!PfWithinBounds((PCHAR)VolumeInfo + Trace->VolumeInfoSize - 1,
  12768. Trace,
  12769. BufferSize)) {
  12770. FailedCheckId = 140;
  12771. goto cleanup;
  12772. }
  12773. //
  12774. // If there are sections, we should have at least one volume.
  12775. //
  12776. if (Trace->NumSections && !Trace->NumVolumes) {
  12777. FailedCheckId = 150;
  12778. goto cleanup;
  12779. }
  12780. //
  12781. // Verify the volume info structures per volume.
  12782. //
  12783. for (VolumeIdx = 0; VolumeIdx < Trace->NumVolumes; VolumeIdx++) {
  12784. //
  12785. // Make sure the whole volume structure is within bounds. Note
  12786. // that VolumeInfo structure contains space for the
  12787. // terminating NUL.
  12788. //
  12789. if (!PfWithinBounds(VolumeInfo, Trace, BufferSize)) {
  12790. FailedCheckId = 155;
  12791. goto cleanup;
  12792. }
  12793. if (!PfWithinBounds((PCHAR) VolumeInfo + sizeof(PF_VOLUME_INFO) - 1,
  12794. Trace,
  12795. BufferSize)) {
  12796. FailedCheckId = 160;
  12797. goto cleanup;
  12798. }
  12799. VolumeInfoSize = sizeof(PF_VOLUME_INFO);
  12800. VolumeInfoSize += VolumeInfo->VolumePathLength * sizeof(WCHAR);
  12801. if (!PfWithinBounds((PCHAR) VolumeInfo + VolumeInfoSize - 1,
  12802. Trace,
  12803. BufferSize)) {
  12804. FailedCheckId = 165;
  12805. goto cleanup;
  12806. }
  12807. //
  12808. // Verify that the volume path string is terminated.
  12809. //
  12810. if (VolumeInfo->VolumePath[VolumeInfo->VolumePathLength] != 0) {
  12811. FailedCheckId = 170;
  12812. goto cleanup;
  12813. }
  12814. //
  12815. // Get the next volume.
  12816. //
  12817. VolumeInfo = (PPF_VOLUME_INFO) ((PCHAR) VolumeInfo + VolumeInfoSize);
  12818. //
  12819. // Make sure VolumeInfo is aligned.
  12820. //
  12821. VolumeInfo = PF_ALIGN_UP(VolumeInfo, _alignof(PF_VOLUME_INFO));
  12822. }
  12823. //
  12824. // We've passed all the checks.
  12825. //
  12826. TraceVerified = TRUE;
  12827. cleanup:
  12828. *FailedCheck = FailedCheckId;
  12829. return TraceVerified;
  12830. }