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.

2410 lines
65 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. pagefile.c
  5. Abstract:
  6. Session manager page file creation routines.
  7. Author:
  8. Silviu Calinoiu (silviuc) 12-Apr-2001
  9. Revision History:
  10. --*/
  11. #include "smsrvp.h"
  12. #include <ntosp.h> // Only for the interlocked functions.
  13. #include "pagefile.h"
  14. //
  15. // issue: silviuc: DbgPrintEx calls active on free builds (controlled by kd_smss_mask)
  16. // We want to do this temporarily for easy debugging in case there are issues.
  17. //
  18. #ifdef KdPrintEx
  19. #undef KdPrintEx
  20. #define KdPrintEx(_x_) DbgPrintEx _x_
  21. #endif
  22. //
  23. // Debugging aids. Since smss is very difficult to debug (cannot attach
  24. // a user mode debugger we need to leave some traces to understand
  25. // postmortem from kernel debugger what went wrong.
  26. //
  27. #define DEBUG_LOG_SIZE 32
  28. typedef struct _DEBUG_LOG_ENTRY {
  29. ULONG Line;
  30. NTSTATUS Status;
  31. PCHAR Description;
  32. PVOID Context;
  33. } DEBUG_LOG_ENTRY;
  34. DEBUG_LOG_ENTRY DebugLog [DEBUG_LOG_SIZE];
  35. LONG DebugLogIndex;
  36. #define DEBUG_LOG_EVENT(_Status, _Message, _Context) { \
  37. \
  38. LONG I = InterlockedIncrement (&DebugLogIndex); \
  39. I %= DEBUG_LOG_SIZE; \
  40. DebugLog[I].Line = __LINE__; \
  41. DebugLog[I].Status = _Status; \
  42. DebugLog[I].Description = _Message; \
  43. DebugLog[I].Context = (PVOID)_Context; \
  44. }
  45. //
  46. // Internal functions
  47. //
  48. VOID
  49. SmpMakeSystemManagedPagingFileDescriptor (
  50. IN OUT PPAGING_FILE_DESCRIPTOR Descriptor
  51. );
  52. VOID
  53. SmpMakeDefaultPagingFileDescriptor (
  54. IN OUT PPAGING_FILE_DESCRIPTOR Descriptor
  55. );
  56. PVOLUME_DESCRIPTOR
  57. SmpSearchVolumeDescriptor (
  58. WCHAR DriveLetter
  59. );
  60. NTSTATUS
  61. SmpValidatePagingFileSizes(
  62. IN PPAGING_FILE_DESCRIPTOR Descriptor
  63. );
  64. NTSTATUS
  65. SmpCreatePagingFileOnAnyDrive(
  66. IN OUT PPAGING_FILE_DESCRIPTOR Descriptor,
  67. IN PLARGE_INTEGER SizeDelta,
  68. IN PLARGE_INTEGER MinimumSize
  69. );
  70. NTSTATUS
  71. SmpCreatePagingFileOnFixedDrive(
  72. IN OUT PPAGING_FILE_DESCRIPTOR Descriptor,
  73. IN PLARGE_INTEGER SizeDelta,
  74. IN PLARGE_INTEGER MinimumSize
  75. );
  76. NTSTATUS
  77. SmpCreateSystemManagedPagingFile (
  78. PPAGING_FILE_DESCRIPTOR Descriptor,
  79. BOOLEAN DecreaseSize
  80. );
  81. NTSTATUS
  82. SmpCreateEmergencyPagingFile (
  83. VOID
  84. );
  85. BOOLEAN
  86. SmpIsPossiblePagingFile (
  87. POBJECT_ATTRIBUTES ObjectAttributes,
  88. PUNICODE_STRING PageFileName
  89. );
  90. NTSTATUS
  91. SmpGetPagingFileSize (
  92. PUNICODE_STRING PageFileName,
  93. PLARGE_INTEGER PageFileSize
  94. );
  95. NTSTATUS
  96. SmpGetVolumeFreeSpace (
  97. PVOLUME_DESCRIPTOR Volume
  98. );
  99. NTSTATUS
  100. SmpDeleteStalePagingFiles (
  101. VOID
  102. );
  103. NTSTATUS
  104. SmpDeletePagingFile (
  105. PUNICODE_STRING PageFileName
  106. );
  107. //
  108. // Standard page file name.
  109. //
  110. #define STANDARD_PAGING_FILE_NAME L"\\??\\?:\\pagefile.sys"
  111. #define STANDARD_DRIVE_LETTER_OFFSET 4
  112. //
  113. // Maximum number of possible paging files. Limit comes from kernel.
  114. //
  115. #define MAXIMUM_NUMBER_OF_PAGING_FILES 16
  116. //
  117. // Minimum free space on disk. Used to avoid a situation where
  118. // a paging file uses the entire disk space.
  119. //
  120. #define MINIMUM_REQUIRED_FREE_SPACE_ON_DISK (32 * 0x100000)
  121. //
  122. // Paging file creation retry constants.
  123. //
  124. #define MINIMUM_PAGING_FILE_SIZE (16 * 0x100000)
  125. #define PAGING_FILE_SIZE_DELTA (16 * 0x100000)
  126. //
  127. // Paging file attributes
  128. //
  129. #define PAGING_FILE_ATTRIBUTES (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)
  130. //
  131. // Volume descriptors
  132. //
  133. LIST_ENTRY SmpVolumeDescriptorList;
  134. //
  135. // Paging file descriptors
  136. //
  137. ULONG SmpNumberOfPagingFiles;
  138. LIST_ENTRY SmpPagingFileDescriptorList;
  139. //
  140. // True if there was at least one paging file registry
  141. // specifier even if it was ill-formed and it did not
  142. // end up as a paging file descriptor.
  143. //
  144. BOOLEAN SmpRegistrySpecifierPresent;
  145. //
  146. // Exception information in case something was raised.
  147. //
  148. ULONG SmpPagingExceptionCode;
  149. PVOID SmpPagingExceptionRecord;
  150. /////////////////////////////////////////////////////////////////////
  151. /////////////////////////////////////////////////////////////////////
  152. /////////////////////////////////////////////////////////////////////
  153. VOID
  154. SmpPagingFileInitialize (
  155. VOID
  156. )
  157. {
  158. InitializeListHead (&SmpPagingFileDescriptorList);
  159. InitializeListHead (&SmpVolumeDescriptorList);
  160. }
  161. NTSTATUS
  162. SmpCreatePagingFileDescriptor(
  163. IN PUNICODE_STRING PagingFileSpecifier
  164. )
  165. /*++
  166. Routine Description:
  167. This function is called during configuration to add a paging file
  168. to the structures describing pagefiles. Later SmpCreatePagingFiles
  169. will create the paging files based on these descriptions.
  170. The format of PagingFileSpec is:
  171. NAME MIN_SIZE MAX_SIZE (sizes specified in Mb)
  172. NAME (system managed paging file)
  173. NAME 0 0 (system managed paging file)
  174. If an error is encountered while converting the string to min/max size
  175. the registry specifier will be ignored.
  176. If the specifier is a duplicate (a `?:\' specifier already present
  177. it will be ignored.
  178. Arguments:
  179. PagingFileSpecifier - Unicode string that specifies the paging file name
  180. and size. The string is allocated during registry read and is assumed
  181. that this function takes ownership of it (w.r.t. freeing etc.).
  182. Return Value:
  183. Status of operation
  184. --*/
  185. {
  186. NTSTATUS Status;
  187. UNICODE_STRING PagingFileName;
  188. UNICODE_STRING Arguments;
  189. ULONG MinSize;
  190. ULONG MaxSize;
  191. PWSTR ArgSave, Arg2;
  192. USHORT I;
  193. BOOLEAN SystemManaged;
  194. BOOLEAN ZeroSizesSpecified;
  195. PPAGING_FILE_DESCRIPTOR Descriptor;
  196. //
  197. // Limit the number of registry specifiers.
  198. //
  199. if (SmpNumberOfPagingFiles >= MAXIMUM_NUMBER_OF_PAGING_FILES) {
  200. KdPrintEx ((DPFLTR_SMSS_ID,
  201. DPFLTR_INFO_LEVEL,
  202. "SMSS:PFILE: Too many paging files specified - %d\n",
  203. SmpNumberOfPagingFiles));
  204. return STATUS_TOO_MANY_PAGING_FILES;
  205. }
  206. //
  207. // Parse the pagefile specification into file name
  208. // and a string with the min and max size (e.g. "min max").
  209. // Necessary buffers for PagingFileName and Arguments are
  210. // allocated in the parsing routine.
  211. //
  212. KdPrintEx ((DPFLTR_SMSS_ID,
  213. DPFLTR_INFO_LEVEL,
  214. "SMSS:PFILE: Paging file specifier `%wZ' \n",
  215. PagingFileSpecifier));
  216. Status = SmpParseCommandLine (PagingFileSpecifier,
  217. NULL,
  218. &PagingFileName,
  219. NULL,
  220. &Arguments);
  221. if (! NT_SUCCESS(Status)) {
  222. DEBUG_LOG_EVENT (Status,
  223. "parsing specified failed",
  224. PagingFileSpecifier);
  225. KdPrintEx ((DPFLTR_SMSS_ID,
  226. DPFLTR_INFO_LEVEL,
  227. "SMSS:PFILE: SmpParseCommandLine(%wZ) failed with status %X \n",
  228. PagingFileSpecifier,
  229. Status));
  230. return Status;
  231. }
  232. //
  233. // We have encountered at least one registry specifier so far.
  234. // This is the right place to initialize this to true because if
  235. // we want no paging file at all the command above will fail and return
  236. // and will leave this variable false. If it is false we will not
  237. // attempt to create an emergency paging file.
  238. //
  239. SmpRegistrySpecifierPresent = TRUE;
  240. //
  241. // Convert the string sizes into integers representing pagefile
  242. // size in Mb. If the `Arguments' string is null or has "0 0" as sizes
  243. // it means we need to create a pagefile using the RAM size.
  244. //
  245. MinSize = 0;
  246. MaxSize = 0;
  247. SystemManaged = FALSE;
  248. ZeroSizesSpecified = FALSE;
  249. if (Arguments.Buffer) {
  250. //
  251. // If we picked up some numbers in the Arguments buffer check out
  252. // if there are only space and strings there.
  253. //
  254. ZeroSizesSpecified = TRUE;
  255. for (I = 0; I < Arguments.Length / sizeof(WCHAR); I += 1) {
  256. if (Arguments.Buffer[I] != L' ' &&
  257. Arguments.Buffer[I] != L'\t' &&
  258. Arguments.Buffer[I] != L'0') {
  259. ZeroSizesSpecified = FALSE;
  260. break;
  261. }
  262. }
  263. }
  264. if (Arguments.Buffer == NULL || ZeroSizesSpecified) {
  265. SystemManaged = TRUE;
  266. }
  267. else {
  268. //
  269. // We need to read two decimal numbers from the Arguments string.
  270. // If we encounter any errors while converting the string to a number
  271. // we will skip the entire specifier.
  272. //
  273. Status = RtlUnicodeStringToInteger (&Arguments, 0, &MinSize);
  274. if (! NT_SUCCESS(Status)) {
  275. DEBUG_LOG_EVENT (Status, NULL, NULL);
  276. RtlFreeUnicodeString (&PagingFileName);
  277. RtlFreeUnicodeString (&Arguments);
  278. return Status;
  279. }
  280. else {
  281. ArgSave = Arguments.Buffer;
  282. Arg2 = ArgSave;
  283. while (*Arg2 != UNICODE_NULL) {
  284. if (*Arg2++ == L' ') {
  285. Arguments.Length -= (USHORT)((PCHAR)Arg2 - (PCHAR)ArgSave);
  286. Arguments.Buffer = Arg2;
  287. Status = RtlUnicodeStringToInteger (&Arguments, 0, &MaxSize);
  288. if (! NT_SUCCESS (Status)) {
  289. DEBUG_LOG_EVENT (Status, NULL, NULL);
  290. RtlFreeUnicodeString (&PagingFileName);
  291. RtlFreeUnicodeString (&Arguments);
  292. return Status;
  293. }
  294. Arguments.Buffer = ArgSave;
  295. break;
  296. }
  297. }
  298. }
  299. }
  300. //
  301. // We do not need the temporary buffer created by the parsing routine
  302. // anymore.
  303. //
  304. RtlFreeUnicodeString (&Arguments);
  305. //
  306. // Save name and values just parsed into a pagefile descriptor
  307. // structure and return. We do not perform any validation on the
  308. // size here because all this will happen when the paging file
  309. // gets created.
  310. //
  311. Descriptor = (PPAGING_FILE_DESCRIPTOR) RtlAllocateHeap (RtlProcessHeap(),
  312. HEAP_ZERO_MEMORY,
  313. sizeof *Descriptor);
  314. if (Descriptor == NULL) {
  315. RtlFreeUnicodeString (&PagingFileName);
  316. return STATUS_NO_MEMORY;
  317. }
  318. Descriptor->Specifier = *PagingFileSpecifier;
  319. Descriptor->Name = PagingFileName;
  320. Descriptor->MinSize.QuadPart = (LONGLONG)MinSize * 0x100000;
  321. Descriptor->MaxSize.QuadPart = (LONGLONG)MaxSize * 0x100000;
  322. Descriptor->SystemManaged = SystemManaged;
  323. Descriptor->Name.Buffer[STANDARD_DRIVE_LETTER_OFFSET] =
  324. RtlUpcaseUnicodeChar (Descriptor->Name.Buffer[STANDARD_DRIVE_LETTER_OFFSET]);
  325. if (Descriptor->Name.Buffer[STANDARD_DRIVE_LETTER_OFFSET] == L'?') {
  326. Descriptor->AnyDrive = 1;
  327. }
  328. //
  329. // Avoid adding a duplicate to the descriptor list.
  330. //
  331. {
  332. PLIST_ENTRY Current;
  333. PPAGING_FILE_DESCRIPTOR FileDescriptor;
  334. BOOLEAN SkipDescriptor;
  335. Current = SmpPagingFileDescriptorList.Flink;
  336. SkipDescriptor = FALSE;
  337. while (Current != &SmpPagingFileDescriptorList) {
  338. FileDescriptor = CONTAINING_RECORD (Current,
  339. PAGING_FILE_DESCRIPTOR,
  340. List);
  341. Current = Current->Flink;
  342. //
  343. // Only one `?:\' descriptor is allowed. All others are skipped.
  344. //
  345. if (FileDescriptor->AnyDrive && Descriptor->AnyDrive) {
  346. SkipDescriptor = TRUE;
  347. break;
  348. }
  349. //
  350. // We allow descriptors on same volume.
  351. //
  352. #if 0
  353. if (FileDescriptor->Name.Buffer[STANDARD_DRIVE_LETTER_OFFSET] ==
  354. Descriptor->Name.Buffer[STANDARD_DRIVE_LETTER_OFFSET]) {
  355. SkipDescriptor = TRUE;
  356. break;
  357. }
  358. #endif
  359. }
  360. if (SkipDescriptor) {
  361. KdPrintEx ((DPFLTR_SMSS_ID,
  362. DPFLTR_INFO_LEVEL,
  363. "SMSS:PFILE: Skipping duplicate specifier `%wZ' \n",
  364. PagingFileSpecifier));
  365. RtlFreeUnicodeString (&PagingFileName);
  366. RtlFreeHeap (RtlProcessHeap(), 0, Descriptor);
  367. return STATUS_INVALID_PARAMETER;
  368. }
  369. }
  370. //
  371. // Finally add new descriptor to the list.
  372. //
  373. InsertTailList (&SmpPagingFileDescriptorList, &(Descriptor->List));
  374. SmpNumberOfPagingFiles += 1;
  375. KdPrintEx ((DPFLTR_SMSS_ID,
  376. DPFLTR_INFO_LEVEL,
  377. "SMSS:PFILE: Created descriptor for `%wZ' (`%wZ') \n",
  378. PagingFileSpecifier,
  379. &(Descriptor->Name)));
  380. return STATUS_SUCCESS;
  381. }
  382. /////////////////////////////////////////////////////////////////////
  383. /////////////////////////////////////////////////////////////////////
  384. /////////////////////////////////////////////////////////////////////
  385. NTSTATUS
  386. SmpCreateVolumeDescriptors (
  387. VOID
  388. )
  389. /*++
  390. Routine description:
  391. This routine iterates all drive letters and creates a small descriptor for
  392. each volume that can host a page file (not a floppy, not removable and not
  393. remote volume). In each descriptor we store the free space available which is
  394. used to compute the volume with largest amount of free space.
  395. Parameters:
  396. None.
  397. Return value:
  398. STATUS_SUCCESS if it managed to find and query at least one volume.
  399. STATUS_UNEXPECTED_IO_ERROR if no volume was found and queried.
  400. --*/
  401. {
  402. WCHAR Drive;
  403. WCHAR StartDrive;
  404. NTSTATUS Status;
  405. UNICODE_STRING VolumePath;
  406. WCHAR Buffer[8];
  407. OBJECT_ATTRIBUTES ObjectAttributes;
  408. HANDLE VolumeHandle;
  409. PVOLUME_DESCRIPTOR Volume;
  410. IO_STATUS_BLOCK IoStatusBlock;
  411. FILE_FS_SIZE_INFORMATION SizeInfo;
  412. FILE_FS_DEVICE_INFORMATION DeviceInfo;
  413. PROCESS_DEVICEMAP_INFORMATION ProcessDeviceMapInfo;
  414. BOOLEAN BootVolumeFound;
  415. //
  416. // Make sure we start with an empty list of volume descriptors.
  417. //
  418. ASSERT (IsListEmpty (&SmpVolumeDescriptorList));
  419. //
  420. // Query ProcessDeviceMap. We are interested in the DriveMap
  421. // bitmap so that we can figure out what drive letters are
  422. // legal.
  423. //
  424. Status = NtQueryInformationProcess (NtCurrentProcess(),
  425. ProcessDeviceMap,
  426. &ProcessDeviceMapInfo.Query,
  427. sizeof(ProcessDeviceMapInfo.Query),
  428. NULL);
  429. if (! NT_SUCCESS(Status)) {
  430. DEBUG_LOG_EVENT (Status, NULL, NULL);
  431. KdPrintEx ((DPFLTR_SMSS_ID,
  432. DPFLTR_INFO_LEVEL,
  433. "SMSS:PFILE: Query(ProcessDeviceMap) failed with status %X \n",
  434. Status));
  435. return Status;
  436. }
  437. //
  438. // Create a template volume path.
  439. //
  440. wcscpy (Buffer, L"\\??\\A:\\");
  441. VolumePath.Buffer = Buffer;
  442. VolumePath.Length = wcslen(VolumePath.Buffer) * sizeof(WCHAR);
  443. VolumePath.MaximumLength = VolumePath.Length + sizeof(WCHAR);
  444. //
  445. // The first possible drive letter.
  446. //
  447. StartDrive = L'C';
  448. #if defined(i386)
  449. //
  450. // PC-9800 Series support.
  451. //
  452. if (IsNEC_98) {
  453. StartDrive = L'A';
  454. }
  455. #endif // defined(i386)
  456. //
  457. // Iterate all possible drive letters.
  458. //
  459. BootVolumeFound = FALSE;
  460. for (Drive = StartDrive; Drive <= L'Z'; Drive += 1) {
  461. //
  462. // Skip invalid drive letters.
  463. //
  464. if ((ProcessDeviceMapInfo.Query.DriveMap & (1 << (Drive - L'A'))) == 0) {
  465. continue;
  466. }
  467. VolumePath.Buffer[STANDARD_DRIVE_LETTER_OFFSET] = Drive;
  468. InitializeObjectAttributes (&ObjectAttributes,
  469. &VolumePath,
  470. OBJ_CASE_INSENSITIVE,
  471. NULL,
  472. NULL);
  473. Status = NtOpenFile (&VolumeHandle,
  474. (ACCESS_MASK)FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | SYNCHRONIZE,
  475. &ObjectAttributes,
  476. &IoStatusBlock,
  477. FILE_SHARE_READ | FILE_SHARE_WRITE,
  478. FILE_SYNCHRONOUS_IO_NONALERT | FILE_DIRECTORY_FILE);
  479. if (! NT_SUCCESS(Status)) {
  480. DEBUG_LOG_EVENT (Status, NULL, NULL);
  481. KdPrintEx ((DPFLTR_SMSS_ID,
  482. DPFLTR_INFO_LEVEL,
  483. "SMSS:PFILE: Open volume `%wZ' failed with status %X \n",
  484. &VolumePath,
  485. Status));
  486. continue;
  487. }
  488. //
  489. // Get volume characteristics.
  490. //
  491. Status = NtQueryVolumeInformationFile (VolumeHandle,
  492. &IoStatusBlock,
  493. &DeviceInfo,
  494. sizeof (DeviceInfo),
  495. FileFsDeviceInformation);
  496. if (! NT_SUCCESS(Status)) {
  497. DEBUG_LOG_EVENT (Status, NULL, NULL);
  498. KdPrintEx ((DPFLTR_SMSS_ID,
  499. DPFLTR_INFO_LEVEL,
  500. "SMSS:PFILE: Query volume `%wZ' (handle %p) for "
  501. "device info failed with status %X \n",
  502. &VolumePath,
  503. VolumeHandle,
  504. Status));
  505. NtClose (VolumeHandle);
  506. continue;
  507. }
  508. //
  509. // Reject volumes that cannot store a paging file.
  510. //
  511. if (DeviceInfo.Characteristics & (FILE_FLOPPY_DISKETTE |
  512. FILE_READ_ONLY_DEVICE |
  513. FILE_REMOTE_DEVICE |
  514. FILE_REMOVABLE_MEDIA )) {
  515. KdPrintEx ((DPFLTR_SMSS_ID,
  516. DPFLTR_INFO_LEVEL,
  517. "SMSS:PFILE: Volume `%wZ' (%X) cannot store a paging file \n",
  518. &VolumePath,
  519. DeviceInfo.Characteristics));
  520. NtClose (VolumeHandle);
  521. continue;
  522. }
  523. //
  524. // Create a volume descriptor entry.
  525. //
  526. Volume = (PVOLUME_DESCRIPTOR) RtlAllocateHeap (RtlProcessHeap(),
  527. HEAP_ZERO_MEMORY,
  528. sizeof *Volume);
  529. if (Volume == NULL) {
  530. KdPrintEx ((DPFLTR_SMSS_ID,
  531. DPFLTR_INFO_LEVEL,
  532. "SMSS:PFILE: Failed to allocate a volume descriptor (%u bytes) \n",
  533. sizeof *Volume));
  534. NtClose (VolumeHandle);
  535. continue;
  536. }
  537. Volume->DriveLetter = Drive;
  538. Volume->DeviceInfo = DeviceInfo;
  539. //
  540. // Check if this is the boot volume.
  541. //
  542. if (RtlUpcaseUnicodeChar(Volume->DriveLetter) ==
  543. RtlUpcaseUnicodeChar(USER_SHARED_DATA->NtSystemRoot[0])) {
  544. ASSERT (BootVolumeFound == FALSE);
  545. Volume->BootVolume = 1;
  546. BootVolumeFound = TRUE;
  547. }
  548. //
  549. // Determine the size parameters of the volume.
  550. //
  551. Status = NtQueryVolumeInformationFile (VolumeHandle,
  552. &IoStatusBlock,
  553. &SizeInfo,
  554. sizeof (SizeInfo),
  555. FileFsSizeInformation);
  556. if (! NT_SUCCESS(Status)) {
  557. DEBUG_LOG_EVENT (Status, NULL, NULL);
  558. KdPrintEx ((DPFLTR_SMSS_ID,
  559. DPFLTR_INFO_LEVEL,
  560. "SMSS:PFILE: Query volume `%wZ' (handle %p) for "
  561. "size failed with status %X \n",
  562. &VolumePath,
  563. VolumeHandle,
  564. Status));
  565. RtlFreeHeap (RtlProcessHeap(), 0, Volume);
  566. NtClose (VolumeHandle);
  567. continue;
  568. }
  569. //
  570. // We do not need the volume handle anymore.
  571. //
  572. NtClose (VolumeHandle);
  573. //
  574. // Compute free space on volume.
  575. //
  576. Volume->FreeSpace = RtlExtendedIntegerMultiply (SizeInfo.AvailableAllocationUnits,
  577. SizeInfo.SectorsPerAllocationUnit);
  578. Volume->FreeSpace = RtlExtendedIntegerMultiply (Volume->FreeSpace,
  579. SizeInfo.BytesPerSector);
  580. //
  581. // Trim a little bit free space on volume to make sure a paging file
  582. // will not use absolutely everything on disk.
  583. //
  584. if (Volume->FreeSpace.QuadPart > MINIMUM_REQUIRED_FREE_SPACE_ON_DISK) {
  585. Volume->FreeSpace.QuadPart -= MINIMUM_REQUIRED_FREE_SPACE_ON_DISK;
  586. } else {
  587. Volume->FreeSpace.QuadPart = 0;
  588. }
  589. //
  590. // Insert the new volume descriptor in decreasing order of
  591. // amount of free space.
  592. //
  593. {
  594. BOOLEAN Inserted = FALSE;
  595. //
  596. // silviuc: insert volumes in letter order instead of free space order
  597. // Note that this is the current NT4, W2000, Whistler behavior.
  598. // We do this because if we insert descriptors in free space order there
  599. // are issues for ?:\pagefile.sys type of descriptors. The way it is
  600. // written the algorithm would have the tendency to create in time
  601. // after several reboot a pagefile on each volume even if only one is
  602. // used each time. To fix this we need smart page file deletion routine
  603. // for stale files. Since nobody uses ?:\ anyway (not yet) we will fix
  604. // these together.
  605. //
  606. #if 0
  607. PLIST_ENTRY Current;
  608. PVOLUME_DESCRIPTOR Descriptor;
  609. Current = SmpVolumeDescriptorList.Flink;
  610. while (Current != &SmpVolumeDescriptorList) {
  611. Descriptor = CONTAINING_RECORD (Current,
  612. VOLUME_DESCRIPTOR,
  613. List);
  614. Current = Current->Flink;
  615. ASSERT (Descriptor->Initialized == TRUE);
  616. ASSERT (Descriptor->DriveLetter >= L'A' && Descriptor->DriveLetter <= L'Z');
  617. if (Descriptor->FreeSpace.QuadPart < Volume->FreeSpace.QuadPart) {
  618. Inserted = TRUE;
  619. Volume->List.Flink = &(Descriptor->List);
  620. Volume->List.Blink = Descriptor->List.Blink;
  621. Descriptor->List.Blink->Flink = &(Volume->List);
  622. Descriptor->List.Blink = &(Volume->List);
  623. break;
  624. }
  625. }
  626. #endif // #if 0
  627. if (! Inserted) {
  628. InsertTailList (&SmpVolumeDescriptorList, &(Volume->List));
  629. }
  630. Volume->Initialized = TRUE;
  631. }
  632. KdPrintEx ((DPFLTR_SMSS_ID, DPFLTR_INFO_LEVEL,
  633. "SMSS:PFILE: Created volume descriptor for`%wZ' \n",
  634. &VolumePath));
  635. }
  636. //
  637. // We should have found at least the boot volume.
  638. //
  639. ASSERT (BootVolumeFound == TRUE);
  640. //
  641. // We should have something in the descriptor list by now.
  642. //
  643. ASSERT (! IsListEmpty (&SmpVolumeDescriptorList));
  644. if (IsListEmpty (&SmpVolumeDescriptorList)) {
  645. Status = STATUS_UNEXPECTED_IO_ERROR;
  646. }
  647. else {
  648. Status = STATUS_SUCCESS;
  649. }
  650. return Status;
  651. }
  652. /////////////////////////////////////////////////////////////////////
  653. /////////////////////////////////////////////////////////////////////
  654. /////////////////////////////////////////////////////////////////////
  655. NTSTATUS
  656. SmpCreatePagingFiles (
  657. VOID
  658. )
  659. /*++
  660. Routine Description:
  661. This function creates pagefiles according to the specifications
  662. read from the registry.
  663. Arguments:
  664. None.
  665. Return Value:
  666. Returns the status of the last pagefile creation operation.
  667. --*/
  668. {
  669. NTSTATUS Status;
  670. PLIST_ENTRY Current;
  671. PPAGING_FILE_DESCRIPTOR Descriptor;
  672. BOOLEAN Created;
  673. LARGE_INTEGER SizeDelta;
  674. LARGE_INTEGER MinimumSize;
  675. //
  676. // We will let the system run without a pagefile if this is
  677. // what the user wants even if it is risky. If we had registry
  678. // specifier but we did not end up with descriptors we postpone
  679. // action for a while. Lower in the function we will deal with
  680. // this case.
  681. //
  682. if (SmpNumberOfPagingFiles == 0 && SmpRegistrySpecifierPresent == FALSE) {
  683. ASSERT (IsListEmpty(&SmpPagingFileDescriptorList));
  684. KdPrintEx ((DPFLTR_SMSS_ID,
  685. DPFLTR_INFO_LEVEL,
  686. "SMSS:PFILE: No paging file was requested \n"));
  687. return STATUS_SUCCESS;
  688. }
  689. //
  690. // Create volume descriptors for all valid volumes. The list of
  691. // volumes is sorted in decreasing order of free space and therefore
  692. // will come in handy when deciding on which volume we can create
  693. // a paging file with highest chances of success.
  694. //
  695. Status = SmpCreateVolumeDescriptors ();
  696. if (! NT_SUCCESS(Status)) {
  697. DEBUG_LOG_EVENT (Status, NULL, NULL);
  698. KdPrintEx ((DPFLTR_SMSS_ID,
  699. DPFLTR_INFO_LEVEL,
  700. "SMSS:PFILE: Failed to create volume descriptors (status %X) \n",
  701. Status));
  702. return Status;
  703. }
  704. //
  705. // Create paging files based on registry descriptors.
  706. //
  707. Current = SmpPagingFileDescriptorList.Flink;
  708. Created = FALSE;
  709. while (Current != &SmpPagingFileDescriptorList) {
  710. Descriptor = CONTAINING_RECORD (Current,
  711. PAGING_FILE_DESCRIPTOR,
  712. List);
  713. Current = Current->Flink;
  714. if (Descriptor->SystemManaged) {
  715. KdPrintEx ((DPFLTR_SMSS_ID,
  716. DPFLTR_INFO_LEVEL,
  717. "SMSS:PFILE: Creating a system managed paging file (`%wZ') \n",
  718. &(Descriptor->Name)));
  719. Status = SmpCreateSystemManagedPagingFile (Descriptor, FALSE);
  720. if (! NT_SUCCESS(Status)) {
  721. //
  722. // Try again but this time allow decrease in size.
  723. //
  724. KdPrintEx ((DPFLTR_SMSS_ID,
  725. DPFLTR_INFO_LEVEL,
  726. "SMSS:PFILE: Trying lower sizes for (`%wZ') \n",
  727. &(Descriptor->Name)));
  728. Status = SmpCreateSystemManagedPagingFile (Descriptor, TRUE);
  729. }
  730. }
  731. else {
  732. SmpValidatePagingFileSizes(Descriptor);
  733. SizeDelta.QuadPart = (LONGLONG)PAGING_FILE_SIZE_DELTA;
  734. KdPrintEx ((DPFLTR_SMSS_ID,
  735. DPFLTR_INFO_LEVEL,
  736. "SMSS:PFILE: Creating a normal paging file (`%wZ') \n",
  737. &(Descriptor->Name)));
  738. if (Descriptor->Name.Buffer[STANDARD_DRIVE_LETTER_OFFSET] == L'?') {
  739. MinimumSize.QuadPart = Descriptor->MinSize.QuadPart;
  740. Status = SmpCreatePagingFileOnAnyDrive (Descriptor,
  741. &SizeDelta,
  742. &MinimumSize);
  743. if (!NT_SUCCESS(Status)) {
  744. KdPrintEx ((DPFLTR_SMSS_ID,
  745. DPFLTR_INFO_LEVEL,
  746. "SMSS:PFILE: Trying lower sizes for (`%wZ') \n",
  747. &(Descriptor->Name)));
  748. MinimumSize.QuadPart = (LONGLONG)MINIMUM_PAGING_FILE_SIZE;
  749. Status = SmpCreatePagingFileOnAnyDrive (Descriptor,
  750. &SizeDelta,
  751. &MinimumSize);
  752. }
  753. }
  754. else {
  755. MinimumSize.QuadPart = (LONGLONG)MINIMUM_PAGING_FILE_SIZE;
  756. Status = SmpCreatePagingFileOnFixedDrive (Descriptor,
  757. &SizeDelta,
  758. &MinimumSize);
  759. }
  760. }
  761. if (NT_SUCCESS(Status)) {
  762. Created = TRUE;
  763. }
  764. }
  765. //
  766. // If we did not manage to create even a single paging file
  767. // assume we had a `?:\pagefile.sys' specifier.
  768. //
  769. if (! Created) {
  770. KdPrintEx ((DPFLTR_SMSS_ID,
  771. DPFLTR_INFO_LEVEL,
  772. "SMSS:PFILE: Creating emergency paging file. \n"));
  773. Status = SmpCreateEmergencyPagingFile ();
  774. }
  775. //
  776. // Delete any paging files that are not going to be used.
  777. //
  778. // silviuc: danger to delete user files having pagefile.sys name
  779. // Plus it does not work for other nonstandard pagefile names.
  780. // To do it the right way we need to have a registry key with the
  781. // names of the pagefiles created on previous boot and delete whatever
  782. // is not used on the current boot.
  783. //
  784. #if 0
  785. SmpDeleteStalePagingFiles ();
  786. #endif
  787. return Status;
  788. }
  789. NTSTATUS
  790. SmpCreateEmergencyPagingFile (
  791. VOID
  792. )
  793. /*++
  794. Routine Description:
  795. This routine creates a paging file of type `?:\pagefile.sys'
  796. (on any drive with system decided size or less). It will create
  797. its own descriptor and put it at first element of paging file
  798. descriptor list.
  799. Arguments:
  800. None.
  801. Return Value:
  802. STATUS_SUCCESS or various error codes.
  803. --*/
  804. {
  805. PPAGING_FILE_DESCRIPTOR Descriptor;
  806. NTSTATUS Status;
  807. CHAR Buffer [64];
  808. Descriptor = (PPAGING_FILE_DESCRIPTOR) RtlAllocateHeap (RtlProcessHeap(),
  809. HEAP_ZERO_MEMORY,
  810. sizeof *Descriptor);
  811. if (Descriptor == NULL) {
  812. return STATUS_NO_MEMORY;
  813. }
  814. RtlInitUnicodeString (&(Descriptor->Name), NULL);
  815. RtlInitUnicodeString (&(Descriptor->Specifier), NULL);
  816. wcscpy ((PWSTR)Buffer, STANDARD_PAGING_FILE_NAME);
  817. ASSERT (sizeof(Buffer) > wcslen(STANDARD_PAGING_FILE_NAME) * sizeof(WCHAR));
  818. Descriptor->Name.Buffer = (PWSTR)Buffer;
  819. Descriptor->Name.Length = wcslen((PWSTR)Buffer) * sizeof(WCHAR);
  820. Descriptor->Name.MaximumLength = Descriptor->Name.Length + sizeof(WCHAR);
  821. Descriptor->Name.Buffer[STANDARD_DRIVE_LETTER_OFFSET] = L'?';
  822. Descriptor->SystemManaged = 1;
  823. Descriptor->Emergency = 1;
  824. Descriptor->AnyDrive = 1;
  825. SmpNumberOfPagingFiles += 1;
  826. InsertHeadList (&SmpPagingFileDescriptorList, &(Descriptor->List));
  827. Status = SmpCreateSystemManagedPagingFile (Descriptor, TRUE);
  828. return Status;
  829. }
  830. NTSTATUS
  831. SmpCreateSystemManagedPagingFile (
  832. PPAGING_FILE_DESCRIPTOR Descriptor,
  833. BOOLEAN DecreaseSize
  834. )
  835. /*++
  836. Routine Description:
  837. This routine creates a system managed paging file.
  838. Arguments:
  839. Descriptor: paging file descriptor.
  840. DecreaseSize: true if size from descriptor can be decreased.
  841. Return Value:
  842. Returns the status of the pagefile creation operation.
  843. --*/
  844. {
  845. LARGE_INTEGER SizeDelta;
  846. LARGE_INTEGER MinimumSize;
  847. NTSTATUS Status;
  848. ASSERT (SmpNumberOfPagingFiles >= 1);
  849. ASSERT (!IsListEmpty(&SmpPagingFileDescriptorList));
  850. ASSERT (Descriptor->SystemManaged == 1);
  851. SmpMakeSystemManagedPagingFileDescriptor (Descriptor);
  852. SmpValidatePagingFileSizes(Descriptor);
  853. SizeDelta.QuadPart = (LONGLONG)PAGING_FILE_SIZE_DELTA;
  854. if (DecreaseSize) {
  855. MinimumSize.QuadPart = (LONGLONG)MINIMUM_PAGING_FILE_SIZE;
  856. }
  857. else {
  858. MinimumSize.QuadPart = Descriptor->MinSize.QuadPart;
  859. }
  860. if (Descriptor->Name.Buffer[STANDARD_DRIVE_LETTER_OFFSET] == L'?') {
  861. Status = SmpCreatePagingFileOnAnyDrive (Descriptor,
  862. &SizeDelta,
  863. &MinimumSize);
  864. }
  865. else {
  866. Status = SmpCreatePagingFileOnFixedDrive (Descriptor,
  867. &SizeDelta,
  868. &MinimumSize);
  869. }
  870. return Status;
  871. }
  872. NTSTATUS
  873. SmpCreatePagingFile (
  874. IN PUNICODE_STRING PageFileName,
  875. IN PLARGE_INTEGER MinimumSize,
  876. IN PLARGE_INTEGER MaximumSize,
  877. IN ULONG Priority OPTIONAL
  878. )
  879. /*++
  880. Routine Description:
  881. This routine is a wrapper around NtCreatePagingFile useful
  882. in case we need to add some debugging code.
  883. Arguments:
  884. Same arguments as NtCreatePagingFile.
  885. Return Value:
  886. Status of the pagefile creation operation.
  887. --*/
  888. {
  889. NTSTATUS Status;
  890. Status = NtCreatePagingFile (PageFileName,
  891. MinimumSize,
  892. MaximumSize,
  893. Priority);
  894. if (! NT_SUCCESS(Status)) {
  895. DEBUG_LOG_EVENT (Status, "failed", PageFileName);
  896. KdPrintEx ((DPFLTR_SMSS_ID,
  897. DPFLTR_INFO_LEVEL,
  898. "SMSS:PFILE: NtCreatePagingFile (%wZ, %I64X, %I64X) failed with %X \n",
  899. PageFileName,
  900. MinimumSize->QuadPart,
  901. MaximumSize->QuadPart,
  902. Status));
  903. }
  904. else {
  905. DEBUG_LOG_EVENT (Status, "success", PageFileName);
  906. KdPrintEx ((DPFLTR_SMSS_ID,
  907. DPFLTR_INFO_LEVEL,
  908. "SMSS:PFILE: NtCreatePagingFile (%wZ, %I64X, %I64X) succeeded. \n",
  909. PageFileName,
  910. MinimumSize->QuadPart,
  911. MaximumSize->QuadPart));
  912. }
  913. return Status;
  914. }
  915. NTSTATUS
  916. SmpCreatePagingFileOnFixedDrive (
  917. IN OUT PPAGING_FILE_DESCRIPTOR Descriptor,
  918. IN PLARGE_INTEGER SizeDelta,
  919. IN PLARGE_INTEGER MinimumSize
  920. )
  921. /*++
  922. Routine Description:
  923. This routine creates a pagefile based on Descriptor. The descriptor
  924. is assumed to have in the Name field a file name prefixed by a drive
  925. letter (e.g. `c:\pagefile.sys'). The function will try to create the
  926. pagefile on the specified drive. If this cannot be done due to space
  927. limitations the function will try smaller sizes down to the absolute
  928. minimum size specified as a parameter.
  929. Note that the function can be forced to not retry with smaller sizes by
  930. specifying a MinimumSize that is equal to Descriptor->MinSize.
  931. Arguments:
  932. Descriptor: paging file descriptor.
  933. SizeDelta: size is decreased by this quantity before retrying
  934. in case we did not manage to create the paging file with requested size.
  935. MinimumSize: the function will try down to this size.
  936. Return Value:
  937. STATUS_SUCCESS if page file has been created. Various error codes
  938. if it fails.
  939. --*/
  940. {
  941. NTSTATUS Status;
  942. PVOLUME_DESCRIPTOR Volume;
  943. LARGE_INTEGER RealFreeSpace;
  944. BOOLEAN FoundPagingFile;
  945. ASSERT (Descriptor->Name.Buffer[STANDARD_DRIVE_LETTER_OFFSET] != L'?');
  946. FoundPagingFile = FALSE;
  947. //
  948. // Get the volume descriptor for this paging file descriptor.
  949. //
  950. Volume = SmpSearchVolumeDescriptor (Descriptor->Name.Buffer[STANDARD_DRIVE_LETTER_OFFSET]);
  951. if (Volume == NULL) {
  952. KdPrintEx ((DPFLTR_SMSS_ID,
  953. DPFLTR_INFO_LEVEL,
  954. "SMSS:PFILE: No volume descriptor for `%wZ' \n",
  955. &(Descriptor->Name)));
  956. return STATUS_INVALID_PARAMETER;
  957. }
  958. //
  959. // Before creating the paging file, check if there is a
  960. // crashdump in it. If so, SmpCheckForCrashDump will
  961. // do whatever is necessary to process the crashdump.
  962. //
  963. if (Volume->BootVolume) {
  964. if (Descriptor->CrashdumpChecked == 0) {
  965. KdPrintEx ((DPFLTR_SMSS_ID,
  966. DPFLTR_INFO_LEVEL,
  967. "SMSS:PFILE: Checking for crash dump in `%wZ' on boot volume \n",
  968. &(Descriptor->Name)));
  969. SmpCheckForCrashDump (&(Descriptor->Name));
  970. Status = SmpGetVolumeFreeSpace (Volume);
  971. if (!NT_SUCCESS(Status)) {
  972. DEBUG_LOG_EVENT (Status, NULL, NULL);
  973. KdPrintEx ((DPFLTR_SMSS_ID,
  974. DPFLTR_INFO_LEVEL,
  975. "SMSS:PFILE: Failed to query free space for boot volume `%wC'\n",
  976. Volume->DriveLetter));
  977. }
  978. Descriptor->CrashdumpChecked = 1;
  979. }
  980. }
  981. else {
  982. KdPrintEx ((DPFLTR_SMSS_ID,
  983. DPFLTR_INFO_LEVEL,
  984. "SMSS:PFILE: Skipping crash dump checking for `%wZ' on non boot volume `%wC' \n",
  985. &(Descriptor->Name),
  986. Volume->DriveLetter));
  987. }
  988. #if 0 // allow multiple pagefiles on the same drive
  989. if (Volume->PagingFileCreated) {
  990. KdPrintEx ((DPFLTR_SMSS_ID,
  991. DPFLTR_INFO_LEVEL,
  992. "SMSS:PFILE: Paging file already created for volume %wc \n",
  993. Volume->DriveLetter));
  994. return STATUS_INVALID_PARAMETER;
  995. }
  996. #endif
  997. //
  998. // Get the size of the future paging file if it exists.
  999. // In case of error (e.g. paging file does not exist yet)
  1000. // RealFreeSpace will contain zero.
  1001. //
  1002. Descriptor->RealMinSize.QuadPart = Descriptor->MinSize.QuadPart;
  1003. Descriptor->RealMaxSize.QuadPart = Descriptor->MaxSize.QuadPart;
  1004. Status = SmpGetPagingFileSize (&(Descriptor->Name),
  1005. &RealFreeSpace);
  1006. if (RealFreeSpace.QuadPart > 0) {
  1007. FoundPagingFile = TRUE;
  1008. }
  1009. KdPrintEx ((DPFLTR_SMSS_ID,
  1010. DPFLTR_INFO_LEVEL,
  1011. "SMSS:PFILE: Detected size %I64X for future paging file `%wZ'\n",
  1012. RealFreeSpace.QuadPart,
  1013. &(Descriptor->Name)));
  1014. //
  1015. // Adjust the free space with the size of the paging file.
  1016. //
  1017. KdPrintEx ((DPFLTR_SMSS_ID,
  1018. DPFLTR_INFO_LEVEL,
  1019. "SMSS:PFILE: Free space on volume `%wC' is %I64X \n",
  1020. Volume->DriveLetter,
  1021. Volume->FreeSpace.QuadPart));
  1022. RealFreeSpace.QuadPart += Volume->FreeSpace.QuadPart;
  1023. if (Descriptor->RealMinSize.QuadPart > RealFreeSpace.QuadPart) {
  1024. Descriptor->RealMinSize.QuadPart = RealFreeSpace.QuadPart;
  1025. }
  1026. if (Descriptor->RealMaxSize.QuadPart > RealFreeSpace.QuadPart) {
  1027. Descriptor->RealMaxSize.QuadPart = RealFreeSpace.QuadPart;
  1028. }
  1029. //
  1030. // Create the paging file.
  1031. //
  1032. KdPrintEx ((DPFLTR_SMSS_ID,
  1033. DPFLTR_INFO_LEVEL,
  1034. "SMSS:PFILE: min %I64X, max %I64X, real min %I64X \n",
  1035. Descriptor->RealMinSize.QuadPart,
  1036. Descriptor->RealMaxSize.QuadPart,
  1037. MinimumSize->QuadPart));
  1038. while (Descriptor->RealMinSize.QuadPart >= MinimumSize->QuadPart) {
  1039. Status = SmpCreatePagingFile (&(Descriptor->Name),
  1040. &(Descriptor->RealMinSize),
  1041. &(Descriptor->RealMaxSize),
  1042. 0);
  1043. if (NT_SUCCESS(Status)) {
  1044. Descriptor->Created = TRUE;
  1045. Volume->PagingFileCreated = TRUE;
  1046. Volume->PagingFileCount += 1;
  1047. break;
  1048. }
  1049. Descriptor->RealMinSize.QuadPart -= SizeDelta->QuadPart;
  1050. }
  1051. if (Descriptor->RealMinSize.QuadPart < MinimumSize->QuadPart) {
  1052. //
  1053. // If are here we did not manage to create a paging file. This
  1054. // can happen for various reasons. For example the initial
  1055. // paging file descriptor had a size that was too small (usually
  1056. // sizes below 16Mb are rejected). For these cases we have to
  1057. // delete any paging file left on the drive.
  1058. //
  1059. if (FoundPagingFile) {
  1060. SmpDeletePagingFile (&(Descriptor->Name));
  1061. }
  1062. KdPrintEx ((DPFLTR_SMSS_ID,
  1063. DPFLTR_INFO_LEVEL,
  1064. "SMSS:PFILE: Failing for min %I64X, max %I64X, real min %I64X \n",
  1065. Descriptor->RealMinSize.QuadPart,
  1066. Descriptor->RealMaxSize.QuadPart,
  1067. MinimumSize->QuadPart));
  1068. return STATUS_DISK_FULL;
  1069. }
  1070. else {
  1071. return Status;
  1072. }
  1073. }
  1074. NTSTATUS
  1075. SmpCreatePagingFileOnAnyDrive(
  1076. IN OUT PPAGING_FILE_DESCRIPTOR Descriptor,
  1077. IN PLARGE_INTEGER SizeDelta,
  1078. IN PLARGE_INTEGER MinimumSize
  1079. )
  1080. /*++
  1081. Routine Description:
  1082. This function creates a pagefile based on Descriptor. The descriptor
  1083. is assumed to have in the Name field a file name prefixed by `?'
  1084. (e.g. `?:\pagefile.sys'). The function will try to create the
  1085. pagefile on any drive. If this cannot be done due to space
  1086. limitations the function will try smaller sizes down to the absolute
  1087. minimum size specified as a parameter.
  1088. Note that the function can be forced to not retry with smaller sizes by
  1089. specifying an AbsoluteMinSizeInMb that is equal to Descriptor->MinSizeInMb.
  1090. Arguments:
  1091. Descriptor: paging file descriptor.
  1092. SizeDelta: size is decreased by this quantity before retrying
  1093. in case we did not manage to create the paging file with requested size.
  1094. MinimumSize: the function will try down to this size.
  1095. Return Value:
  1096. NT_SUCCESS if page file has been created. Various error codes if it
  1097. fails.
  1098. --*/
  1099. {
  1100. PLIST_ENTRY Current;
  1101. PVOLUME_DESCRIPTOR Volume;
  1102. NTSTATUS Status;
  1103. ASSERT (Descriptor->Name.Buffer[STANDARD_DRIVE_LETTER_OFFSET] == L'?');
  1104. //
  1105. // Iterate the sorted list of volume descriptors.
  1106. //
  1107. Current = SmpVolumeDescriptorList.Flink;
  1108. Status = STATUS_DISK_FULL;
  1109. while (Current != &SmpVolumeDescriptorList) {
  1110. Volume = CONTAINING_RECORD (Current,
  1111. VOLUME_DESCRIPTOR,
  1112. List);
  1113. Current = Current->Flink;
  1114. ASSERT (Volume->Initialized == TRUE);
  1115. ASSERT (Volume->DriveLetter >= L'A' && Volume->DriveLetter <= L'Z');
  1116. Descriptor->Name.Buffer[STANDARD_DRIVE_LETTER_OFFSET] = Volume->DriveLetter;
  1117. Status = SmpCreatePagingFileOnFixedDrive (Descriptor,
  1118. SizeDelta,
  1119. MinimumSize);
  1120. if (NT_SUCCESS(Status)) {
  1121. break;
  1122. }
  1123. Descriptor->Name.Buffer[STANDARD_DRIVE_LETTER_OFFSET] = L'?';
  1124. }
  1125. return Status;
  1126. }
  1127. NTSTATUS
  1128. SmpValidatePagingFileSizes(
  1129. IN PPAGING_FILE_DESCRIPTOR Descriptor
  1130. )
  1131. /*++
  1132. Routine Description:
  1133. This function validates the min/max paging file sizes specified.
  1134. It takes into consideration the architecture, the type of kernel
  1135. (pae vs. nonpae), multiboot scenarios, etc..
  1136. Arguments:
  1137. Descriptor: paging file descriptor.
  1138. Return Value:
  1139. NT_SUCCESS if we have succesfully decided what are the proper sizes.
  1140. In case of success the Min/MaxSize fileds of the paging file descriptor
  1141. will get filled with proper sizes.
  1142. --*/
  1143. {
  1144. NTSTATUS Status;
  1145. ULONGLONG MinSize;
  1146. ULONGLONG MaxSize;
  1147. const ULONGLONG SIZE_1_MB = 0x100000;
  1148. const ULONGLONG SIZE_1_GB = 1024 * SIZE_1_MB;
  1149. const ULONGLONG SIZE_1_TB = 1024 * SIZE_1_GB;
  1150. BOOLEAN SizeTrimmed = FALSE;;
  1151. KdPrintEx ((DPFLTR_SMSS_ID,
  1152. DPFLTR_INFO_LEVEL,
  1153. "SMSS:PFILE: Validating sizes for `%wZ' %I64X %I64X\n",
  1154. &(Descriptor->Name),
  1155. Descriptor->MinSize.QuadPart,
  1156. Descriptor->MaxSize.QuadPart));
  1157. MinSize = (ULONGLONG)(Descriptor->MinSize.QuadPart);
  1158. MaxSize = (ULONGLONG)(Descriptor->MaxSize.QuadPart);
  1159. //
  1160. // Make sure max is bigger than min.
  1161. //
  1162. if (MinSize > MaxSize) {
  1163. MaxSize = MinSize;
  1164. }
  1165. //
  1166. // Check if min/max sizes are not too big.
  1167. //
  1168. Status = STATUS_SUCCESS;
  1169. #if defined(i386)
  1170. //
  1171. // X86 32 bits supports max 4095 Mb per pagefile
  1172. // X86 PAE supports 16 Tb per pagefile
  1173. //
  1174. // If USER_SHARED_DATA structure has not been initialized
  1175. // we will use the standard x86 limits.
  1176. //
  1177. if (USER_SHARED_DATA && (USER_SHARED_DATA->ProcessorFeatures[PF_PAE_ENABLED])) {
  1178. //
  1179. // We are in PAE mode.
  1180. //
  1181. if (MinSize > 16 * SIZE_1_TB) {
  1182. SizeTrimmed = TRUE;
  1183. MinSize = 16 * SIZE_1_TB;
  1184. }
  1185. if (MaxSize > 16 * SIZE_1_TB) {
  1186. SizeTrimmed = TRUE;
  1187. MaxSize = 16 * SIZE_1_TB;
  1188. }
  1189. }
  1190. else {
  1191. //
  1192. // Standard x86 mode.
  1193. //
  1194. if (MinSize > 4095 * SIZE_1_MB) {
  1195. SizeTrimmed = TRUE;
  1196. MinSize = 4095 * SIZE_1_MB;
  1197. }
  1198. if (MaxSize > 4095 * SIZE_1_MB) {
  1199. SizeTrimmed = TRUE;
  1200. MaxSize = 4095 * SIZE_1_MB;
  1201. }
  1202. }
  1203. #elif defined(_WIN64)
  1204. //
  1205. // IA64, AXP64 support 32 Tb per pagefile
  1206. // AMD64 supports 16 Tb per pagefile
  1207. //
  1208. // We will use for all cases 16 Tb which is anyway a huge
  1209. // number for the foreseeable future.
  1210. //
  1211. if (MinSize > 16 * SIZE_1_TB) {
  1212. SizeTrimmed = TRUE;
  1213. MinSize = 16 * SIZE_1_TB;
  1214. }
  1215. if (MaxSize > 16 * SIZE_1_TB) {
  1216. SizeTrimmed = TRUE;
  1217. MaxSize = 16 * SIZE_1_TB;
  1218. }
  1219. #else
  1220. //
  1221. // If we did not recognize the architecture we play it
  1222. // as safe as possible.
  1223. //
  1224. if (MinSize > 4095 * SIZE_1_MB) {
  1225. SizeTrimmed = TRUE;
  1226. MinSize = 4095 * SIZE_1_MB;
  1227. }
  1228. if (MaxSize > 4095 * SIZE_1_MB) {
  1229. SizeTrimmed = TRUE;
  1230. MaxSize = 4095 * SIZE_1_MB;
  1231. }
  1232. #endif
  1233. if (SizeTrimmed) {
  1234. KdPrintEx ((DPFLTR_SMSS_ID,
  1235. DPFLTR_INFO_LEVEL,
  1236. "SMSS:PFILE: Trimmed size of `%wZ' to maximum allowed \n",
  1237. &(Descriptor->Name)));
  1238. }
  1239. if (SizeTrimmed) {
  1240. Descriptor->SizeTrimmed = 1;
  1241. }
  1242. Descriptor->MinSize.QuadPart = (LONGLONG)MinSize;
  1243. Descriptor->MaxSize.QuadPart = (LONGLONG)MaxSize;
  1244. return Status;
  1245. }
  1246. PVOLUME_DESCRIPTOR
  1247. SmpSearchVolumeDescriptor (
  1248. WCHAR DriveLetter
  1249. )
  1250. {
  1251. PLIST_ENTRY Current;
  1252. PVOLUME_DESCRIPTOR Volume;
  1253. DriveLetter = RtlUpcaseUnicodeChar(DriveLetter);
  1254. Current = SmpVolumeDescriptorList.Flink;
  1255. while (Current != &SmpVolumeDescriptorList) {
  1256. Volume = CONTAINING_RECORD (Current,
  1257. VOLUME_DESCRIPTOR,
  1258. List);
  1259. Current = Current->Flink;
  1260. ASSERT (Volume->Initialized == TRUE);
  1261. ASSERT (Volume->DriveLetter >= L'A' && Volume->DriveLetter <= L'Z');
  1262. if (Volume->DriveLetter == DriveLetter) {
  1263. return Volume;
  1264. }
  1265. }
  1266. return NULL;
  1267. }
  1268. VOID
  1269. SmpMakeSystemManagedPagingFileDescriptor (
  1270. IN OUT PPAGING_FILE_DESCRIPTOR Descriptor
  1271. )
  1272. {
  1273. NTSTATUS Status;
  1274. ULONGLONG MinSize;
  1275. ULONGLONG MaxSize;
  1276. SYSTEM_BASIC_INFORMATION SystemInformation;
  1277. ULONGLONG Ram;
  1278. const ULONGLONG SIZE_1_MB = 0x100000;
  1279. const ULONGLONG SIZE_1_GB = 1024 * SIZE_1_MB;
  1280. Status = NtQuerySystemInformation (SystemBasicInformation,
  1281. &SystemInformation,
  1282. sizeof SystemInformation,
  1283. NULL);
  1284. if (! NT_SUCCESS (Status)) {
  1285. DEBUG_LOG_EVENT (Status, NULL, NULL);
  1286. KdPrintEx ((DPFLTR_SMSS_ID,
  1287. DPFLTR_INFO_LEVEL,
  1288. "SMSS:PFILE: NtQuerySystemInformation failed with %x \n",
  1289. Status));
  1290. SmpMakeDefaultPagingFileDescriptor (Descriptor);
  1291. }
  1292. else {
  1293. Ram = (ULONGLONG)(SystemInformation.NumberOfPhysicalPages) *
  1294. SystemInformation.PageSize;
  1295. //
  1296. // RAM Min Max
  1297. //
  1298. // < 1Gb 1.5 x 3 x
  1299. // >= 1Gb 1 x 3 x
  1300. //
  1301. if (Ram < SIZE_1_GB) {
  1302. MinSize = 3 * Ram / 2;
  1303. MaxSize = 3 * Ram;
  1304. }
  1305. else {
  1306. MinSize = Ram;
  1307. MaxSize = 3 * Ram;
  1308. }
  1309. Descriptor->MinSize.QuadPart = (LONGLONG)MinSize;
  1310. Descriptor->MaxSize.QuadPart = (LONGLONG)MaxSize;
  1311. Descriptor->SystemManaged = 1;
  1312. }
  1313. }
  1314. VOID
  1315. SmpMakeDefaultPagingFileDescriptor (
  1316. IN OUT PPAGING_FILE_DESCRIPTOR Descriptor
  1317. )
  1318. /*++
  1319. Routine Description:
  1320. This function is called if we failed to come up with paging
  1321. file descriptors. It will create some default settings that will
  1322. be used to create a pagefile.
  1323. Arguments:
  1324. EmergencyDesriptor: pointer to one paging file descriptor that will
  1325. be filled with some emergency values.
  1326. Return Value:
  1327. None. This function will always succeed.
  1328. --*/
  1329. {
  1330. const ULONGLONG SIZE_1_MB = 0x100000;
  1331. Descriptor->MinSize.QuadPart = (LONGLONG)128 * SIZE_1_MB;
  1332. Descriptor->MaxSize.QuadPart = (LONGLONG)128 * SIZE_1_MB;
  1333. Descriptor->DefaultSize = 1;
  1334. }
  1335. NTSTATUS
  1336. SmpDeleteStalePagingFiles (
  1337. VOID
  1338. )
  1339. /*++
  1340. Routine Description:
  1341. This routine iterates all volumes from volume descriptor list and
  1342. deletes stale paging files. If we created a new one on top of the old
  1343. one we will skip of course.
  1344. Arguments:
  1345. None.
  1346. Return Value:
  1347. STATUS_SUCCESS or various error codes.
  1348. --*/
  1349. {
  1350. PLIST_ENTRY Current;
  1351. PVOLUME_DESCRIPTOR Volume;
  1352. UNICODE_STRING PageFileName;
  1353. CHAR Buffer [64];
  1354. OBJECT_ATTRIBUTES ObjectAttributes;
  1355. IO_STATUS_BLOCK IoStatusBlock;
  1356. HANDLE PageFileHandle;
  1357. NTSTATUS Status;
  1358. FILE_DISPOSITION_INFORMATION Disposition;
  1359. Current = SmpVolumeDescriptorList.Flink;
  1360. while (Current != &SmpVolumeDescriptorList) {
  1361. Volume = CONTAINING_RECORD (Current,
  1362. VOLUME_DESCRIPTOR,
  1363. List);
  1364. Current = Current->Flink;
  1365. ASSERT (Volume->Initialized == TRUE);
  1366. ASSERT (Volume->DriveLetter >= L'A' && Volume->DriveLetter <= L'Z');
  1367. if (Volume->PagingFilePresent == 1 && Volume->PagingFileCreated == 0) {
  1368. wcscpy ((PWSTR)Buffer, STANDARD_PAGING_FILE_NAME);
  1369. ASSERT (sizeof(Buffer) > wcslen(STANDARD_PAGING_FILE_NAME) * sizeof(WCHAR));
  1370. PageFileName.Buffer = (PWSTR)Buffer;
  1371. PageFileName.Length = wcslen((PWSTR)Buffer) * sizeof(WCHAR);
  1372. PageFileName.MaximumLength = PageFileName.Length + sizeof(WCHAR);
  1373. PageFileName.Buffer[STANDARD_DRIVE_LETTER_OFFSET] = Volume->DriveLetter;
  1374. InitializeObjectAttributes (&ObjectAttributes,
  1375. &PageFileName,
  1376. OBJ_CASE_INSENSITIVE,
  1377. NULL,
  1378. NULL);
  1379. //
  1380. // We check the paging file attributes and if these are not the typical
  1381. // ones (hidden and system) we will skip this file.
  1382. //
  1383. if (! SmpIsPossiblePagingFile (&ObjectAttributes, &PageFileName)) {
  1384. continue;
  1385. }
  1386. //
  1387. // Open the paging file for deletion.
  1388. //
  1389. Status = NtOpenFile (&PageFileHandle,
  1390. (ACCESS_MASK)DELETE,
  1391. &ObjectAttributes,
  1392. &IoStatusBlock,
  1393. FILE_SHARE_DELETE |
  1394. FILE_SHARE_READ |
  1395. FILE_SHARE_WRITE,
  1396. FILE_NON_DIRECTORY_FILE);
  1397. if (! NT_SUCCESS (Status)) {
  1398. DEBUG_LOG_EVENT (Status, NULL, NULL);
  1399. KdPrintEx ((DPFLTR_SMSS_ID,
  1400. DPFLTR_INFO_LEVEL,
  1401. "SMSS:PFILE: Failed to open page file `%wZ' for "
  1402. "deletion (status %X)\n",
  1403. &PageFileName,
  1404. Status));
  1405. }
  1406. else {
  1407. Disposition.DeleteFile = TRUE;
  1408. Status = NtSetInformationFile (PageFileHandle,
  1409. &IoStatusBlock,
  1410. &Disposition,
  1411. sizeof( Disposition ),
  1412. FileDispositionInformation);
  1413. if (NT_SUCCESS(Status)) {
  1414. DEBUG_LOG_EVENT (Status, NULL, NULL);
  1415. KdPrintEx ((DPFLTR_SMSS_ID,
  1416. DPFLTR_INFO_LEVEL,
  1417. "SMSS:PFILE: Deleted stale paging file - %wZ\n",
  1418. &PageFileName));
  1419. }
  1420. else {
  1421. DEBUG_LOG_EVENT (Status, NULL, NULL);
  1422. KdPrintEx ((DPFLTR_SMSS_ID,
  1423. DPFLTR_INFO_LEVEL,
  1424. "SMSS:PFILE: Failed to delete page file `%wZ' "
  1425. "(status %X)\n",
  1426. &PageFileName,
  1427. Status));
  1428. }
  1429. NtClose(PageFileHandle);
  1430. }
  1431. }
  1432. }
  1433. return STATUS_SUCCESS;
  1434. }
  1435. NTSTATUS
  1436. SmpDeletePagingFile (
  1437. PUNICODE_STRING PageFileName
  1438. )
  1439. /*++
  1440. Routine Description:
  1441. This routine deletes the paging file described by name.
  1442. Arguments:
  1443. Descriptor: Paging file name.
  1444. Return Value:
  1445. STATUS_SUCCESS or various error codes.
  1446. --*/
  1447. {
  1448. OBJECT_ATTRIBUTES ObjectAttributes;
  1449. IO_STATUS_BLOCK IoStatusBlock;
  1450. HANDLE PageFileHandle;
  1451. NTSTATUS Status;
  1452. FILE_DISPOSITION_INFORMATION Disposition;
  1453. InitializeObjectAttributes (&ObjectAttributes,
  1454. PageFileName,
  1455. OBJ_CASE_INSENSITIVE,
  1456. NULL,
  1457. NULL);
  1458. //
  1459. // Open the paging file for deletion.
  1460. //
  1461. Status = NtOpenFile (&PageFileHandle,
  1462. (ACCESS_MASK)DELETE,
  1463. &ObjectAttributes,
  1464. &IoStatusBlock,
  1465. FILE_SHARE_DELETE |
  1466. FILE_SHARE_READ |
  1467. FILE_SHARE_WRITE,
  1468. FILE_NON_DIRECTORY_FILE);
  1469. if (! NT_SUCCESS (Status)) {
  1470. DEBUG_LOG_EVENT (Status, NULL, NULL);
  1471. KdPrintEx ((DPFLTR_SMSS_ID,
  1472. DPFLTR_INFO_LEVEL,
  1473. "SMSS:PFILE: Failed to open for deletion page file `%wZ' "
  1474. "(status %X)\n",
  1475. PageFileName,
  1476. Status));
  1477. }
  1478. else {
  1479. Disposition.DeleteFile = TRUE;
  1480. Status = NtSetInformationFile (PageFileHandle,
  1481. &IoStatusBlock,
  1482. &Disposition,
  1483. sizeof( Disposition ),
  1484. FileDispositionInformation);
  1485. if (NT_SUCCESS(Status)) {
  1486. DEBUG_LOG_EVENT (Status, NULL, NULL);
  1487. KdPrintEx ((DPFLTR_SMSS_ID,
  1488. DPFLTR_INFO_LEVEL,
  1489. "SMSS:PFILE: Deleted stale paging file - %wZ\n",
  1490. PageFileName));
  1491. }
  1492. else {
  1493. DEBUG_LOG_EVENT (Status, NULL, NULL);
  1494. KdPrintEx ((DPFLTR_SMSS_ID,
  1495. DPFLTR_INFO_LEVEL,
  1496. "SMSS:PFILE: Failed to delete page file `%wZ' "
  1497. "(status %X)\n",
  1498. PageFileName,
  1499. Status));
  1500. }
  1501. NtClose(PageFileHandle);
  1502. }
  1503. return Status;
  1504. }
  1505. BOOLEAN
  1506. SmpIsPossiblePagingFile (
  1507. POBJECT_ATTRIBUTES ObjectAttributes,
  1508. PUNICODE_STRING PageFileName
  1509. )
  1510. /*++
  1511. Routine Description:
  1512. This routine checks if the file passed as a parameter has typical
  1513. paging file attributes (system and hidden). If not then it is likely
  1514. that (a) user changed attirbutes or (b) this is not a pagefile but rather
  1515. a user file with this name.
  1516. Arguments:
  1517. ObjectAttributes
  1518. PageFileName
  1519. Return Value:
  1520. True if this is likely to be a paging file, false otherwise.
  1521. --*/
  1522. {
  1523. IO_STATUS_BLOCK IoStatusBlock;
  1524. HANDLE PageFileHandle;
  1525. NTSTATUS Status;
  1526. FILE_BASIC_INFORMATION FileInfo;
  1527. Status = NtOpenFile (&PageFileHandle,
  1528. (ACCESS_MASK)FILE_READ_ATTRIBUTES | SYNCHRONIZE,
  1529. ObjectAttributes,
  1530. &IoStatusBlock,
  1531. FILE_SHARE_READ | FILE_SHARE_WRITE,
  1532. FILE_SYNCHRONOUS_IO_NONALERT);
  1533. if (! NT_SUCCESS( Status )) {
  1534. DEBUG_LOG_EVENT (Status, NULL, NULL);
  1535. KdPrintEx ((DPFLTR_SMSS_ID,
  1536. DPFLTR_INFO_LEVEL,
  1537. "SMSS:PFILE: Failed to open for query file `%wZ' with status %X \n",
  1538. PageFileName,
  1539. Status));
  1540. return FALSE;
  1541. }
  1542. Status = NtQueryInformationFile (PageFileHandle,
  1543. &IoStatusBlock,
  1544. &FileInfo,
  1545. sizeof (FileInfo),
  1546. FileBasicInformation);
  1547. if (! NT_SUCCESS( Status )) {
  1548. DEBUG_LOG_EVENT (Status, NULL, NULL);
  1549. KdPrintEx ((DPFLTR_SMSS_ID,
  1550. DPFLTR_INFO_LEVEL,
  1551. "SMSS:PFILE: Failed to query for attributes file `%wZ' with status %X \n",
  1552. PageFileName,
  1553. Status));
  1554. NtClose (PageFileHandle);
  1555. return FALSE;
  1556. }
  1557. //
  1558. // Close handle since we do not need it anymore.
  1559. //
  1560. NtClose (PageFileHandle);
  1561. //
  1562. // If the attributes are not system and hidden this is not likely to be a
  1563. // pagefile. Either the user changed attributes on the pagefile or it is
  1564. // not a pagefile at all.
  1565. //
  1566. if ((FileInfo.FileAttributes & PAGING_FILE_ATTRIBUTES) != PAGING_FILE_ATTRIBUTES) {
  1567. return FALSE;
  1568. }
  1569. else {
  1570. return TRUE;
  1571. }
  1572. }
  1573. NTSTATUS
  1574. SmpGetPagingFileSize (
  1575. PUNICODE_STRING PageFileName,
  1576. PLARGE_INTEGER PageFileSize
  1577. )
  1578. /*++
  1579. Routine Description:
  1580. This routine checks if the file passed as a parameter exists and gets
  1581. its file size. This will be used to correct the free space available
  1582. on a volume.
  1583. Arguments:
  1584. PageFileName
  1585. PageFileSize
  1586. Return Value:
  1587. STATUS_SUCCESS if we managed to open a paging file and query the size.
  1588. --*/
  1589. {
  1590. IO_STATUS_BLOCK IoStatusBlock;
  1591. HANDLE PageFileHandle;
  1592. NTSTATUS Status;
  1593. OBJECT_ATTRIBUTES ObjectAttributes;
  1594. FILE_STANDARD_INFORMATION FileSizeInfo;
  1595. KdPrintEx ((DPFLTR_SMSS_ID,
  1596. DPFLTR_INFO_LEVEL,
  1597. "SMSS:PFILE: Trying to get size for `%wZ'\n",
  1598. PageFileName));
  1599. PageFileSize->QuadPart = 0;
  1600. InitializeObjectAttributes (&ObjectAttributes,
  1601. PageFileName,
  1602. OBJ_CASE_INSENSITIVE,
  1603. NULL,
  1604. NULL);
  1605. Status = NtOpenFile (&PageFileHandle,
  1606. (ACCESS_MASK)FILE_READ_ATTRIBUTES | SYNCHRONIZE,
  1607. &ObjectAttributes,
  1608. &IoStatusBlock,
  1609. FILE_SHARE_READ | FILE_SHARE_WRITE,
  1610. FILE_SYNCHRONOUS_IO_NONALERT);
  1611. if (! NT_SUCCESS( Status )) {
  1612. DEBUG_LOG_EVENT (Status, NULL, PageFileName);
  1613. return Status;
  1614. }
  1615. Status = NtQueryInformationFile (PageFileHandle,
  1616. &IoStatusBlock,
  1617. &FileSizeInfo,
  1618. sizeof (FileSizeInfo),
  1619. FileStandardInformation);
  1620. if (! NT_SUCCESS( Status )) {
  1621. DEBUG_LOG_EVENT (Status, NULL, PageFileName);
  1622. KdPrintEx ((DPFLTR_SMSS_ID,
  1623. DPFLTR_INFO_LEVEL,
  1624. "SMSS:PFILE: Failed query for size potential pagefile `%wZ' with status %X \n",
  1625. PageFileName,
  1626. Status));
  1627. NtClose (PageFileHandle);
  1628. return Status;
  1629. }
  1630. //
  1631. // We do not need the paging file handle anymore.
  1632. //
  1633. NtClose (PageFileHandle);
  1634. //
  1635. // Return size.
  1636. //
  1637. PageFileSize->QuadPart = FileSizeInfo.AllocationSize.QuadPart;
  1638. return STATUS_SUCCESS;
  1639. }
  1640. NTSTATUS
  1641. SmpGetVolumeFreeSpace (
  1642. PVOLUME_DESCRIPTOR Volume
  1643. )
  1644. /*++
  1645. Routine Description:
  1646. This routine computes the amount of free space on a volume.
  1647. Arguments:
  1648. Volume
  1649. FreeSpace
  1650. Return Value:
  1651. STATUS_SUCCESS if we managed to query the free space size.
  1652. --*/
  1653. {
  1654. NTSTATUS Status;
  1655. UNICODE_STRING VolumePath;
  1656. WCHAR Buffer[8];
  1657. OBJECT_ATTRIBUTES ObjectAttributes;
  1658. HANDLE VolumeHandle;
  1659. IO_STATUS_BLOCK IoStatusBlock;
  1660. FILE_FS_SIZE_INFORMATION SizeInfo;
  1661. //
  1662. // This function gets called only for boot volumes that contain
  1663. // crashdumps. Crashdump processing will modify the free space
  1664. // computed when the volume descriptor has been created.
  1665. //
  1666. ASSERT (Volume->BootVolume == 1);
  1667. //
  1668. // Create a template volume path.
  1669. //
  1670. wcscpy (Buffer, L"\\??\\A:\\");
  1671. VolumePath.Buffer = Buffer;
  1672. VolumePath.Length = wcslen(VolumePath.Buffer) * sizeof(WCHAR);
  1673. VolumePath.MaximumLength = VolumePath.Length + sizeof(WCHAR);
  1674. VolumePath.Buffer[STANDARD_DRIVE_LETTER_OFFSET] = Volume->DriveLetter;
  1675. KdPrintEx ((DPFLTR_SMSS_ID,
  1676. DPFLTR_INFO_LEVEL,
  1677. "SMSS:PFILE: Querying volume `%wZ' for free space \n",
  1678. &VolumePath));
  1679. InitializeObjectAttributes (&ObjectAttributes,
  1680. &VolumePath,
  1681. OBJ_CASE_INSENSITIVE,
  1682. NULL,
  1683. NULL);
  1684. Status = NtOpenFile (&VolumeHandle,
  1685. (ACCESS_MASK)FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | SYNCHRONIZE,
  1686. &ObjectAttributes,
  1687. &IoStatusBlock,
  1688. FILE_SHARE_READ | FILE_SHARE_WRITE,
  1689. FILE_SYNCHRONOUS_IO_NONALERT | FILE_DIRECTORY_FILE);
  1690. if (! NT_SUCCESS(Status)) {
  1691. DEBUG_LOG_EVENT (Status, NULL, NULL);
  1692. KdPrintEx ((DPFLTR_SMSS_ID,
  1693. DPFLTR_INFO_LEVEL,
  1694. "SMSS:PFILE: Open volume `%wZ' failed with status %X \n",
  1695. &VolumePath,
  1696. Status));
  1697. return Status;
  1698. }
  1699. //
  1700. // Determine the size parameters of the volume.
  1701. //
  1702. Status = NtQueryVolumeInformationFile (VolumeHandle,
  1703. &IoStatusBlock,
  1704. &SizeInfo,
  1705. sizeof (SizeInfo),
  1706. FileFsSizeInformation);
  1707. if (! NT_SUCCESS(Status)) {
  1708. DEBUG_LOG_EVENT (Status, NULL, NULL);
  1709. KdPrintEx ((DPFLTR_SMSS_ID,
  1710. DPFLTR_INFO_LEVEL,
  1711. "SMSS:PFILE: Query volume `%wZ' (handle %p) for "
  1712. "size failed with status %X \n",
  1713. &VolumePath,
  1714. VolumeHandle,
  1715. Status));
  1716. NtClose (VolumeHandle);
  1717. return Status;
  1718. }
  1719. //
  1720. // We do not need the volume handle anymore.
  1721. //
  1722. NtClose (VolumeHandle);
  1723. //
  1724. // Compute free space on volume.
  1725. //
  1726. Volume->FreeSpace = RtlExtendedIntegerMultiply (SizeInfo.AvailableAllocationUnits,
  1727. SizeInfo.SectorsPerAllocationUnit);
  1728. Volume->FreeSpace = RtlExtendedIntegerMultiply (Volume->FreeSpace,
  1729. SizeInfo.BytesPerSector);
  1730. //
  1731. // Trim a little bit free space on volume to make sure a paging file
  1732. // will not use absolutely everything on disk.
  1733. //
  1734. if (Volume->FreeSpace.QuadPart > MINIMUM_REQUIRED_FREE_SPACE_ON_DISK) {
  1735. Volume->FreeSpace.QuadPart -= MINIMUM_REQUIRED_FREE_SPACE_ON_DISK;
  1736. }
  1737. else {
  1738. Volume->FreeSpace.QuadPart = 0;
  1739. }
  1740. return STATUS_SUCCESS;
  1741. }
  1742. ULONG
  1743. SmpPagingFileExceptionFilter (
  1744. ULONG ExceptionCode,
  1745. PVOID ExceptionRecord
  1746. )
  1747. /*++
  1748. Routine Description:
  1749. This routine filters any exception that might occur in paging
  1750. file creation code paths.
  1751. Arguments:
  1752. ExceptionCode
  1753. ExceptionRecord
  1754. Return Value:
  1755. EXCEPTION_CONTINUE_SEARCH for most of the cases since we want smss
  1756. to crash so that we can investigate what was going on.
  1757. --*/
  1758. {
  1759. //
  1760. // Save exception information for debugging.
  1761. //
  1762. SmpPagingExceptionCode = ExceptionCode;
  1763. SmpPagingExceptionRecord = ExceptionRecord;
  1764. //
  1765. // We print this message no matter what because we want to know
  1766. // what happened if smss crashes.
  1767. //
  1768. DbgPrint ("SMSS:PFILE: unexpected exception %X with record %p \n",
  1769. ExceptionCode,
  1770. ExceptionRecord);
  1771. #if DBG
  1772. DbgBreakPoint ();
  1773. #endif
  1774. return EXCEPTION_CONTINUE_SEARCH;
  1775. }