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

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