2 * PROJECT: ReactOS Windows-Compatible Session Manager
3 * LICENSE: BSD 2-Clause License
4 * FILE: base/system/smss/pagefile.c
5 * PURPOSE: Main SMSS Code
6 * PROGRAMMERS: Alex Ionescu
9 /* INCLUDES *******************************************************************/
16 /* GLOBALS ********************************************************************/
21 #define STANDARD_PAGING_FILE_NAME L"\\??\\?:\\pagefile.sys"
22 #define STANDARD_DRIVE_LETTER_OFFSET 4
23 #define MAX_PAGING_FILES 16 // See also ntoskrnl/include/internal/mm.h
24 #define MEGABYTE (1024 * 1024)
26 /* Minimum pagefile size is 256 pages (1 MB) */
27 // #define MINIMUM_PAGEFILE_SIZE (256ULL * PAGE_SIZE)
29 /* Maximum pagefile sizes for different architectures */
30 #define GIGABYTE (1024ULL * MEGABYTE)
31 #define TERABYTE (1024ULL * GIGABYTE)
33 // NOTE: No changes for NTDDI_WIN10
34 #if (NTDDI_VERSION >= NTDDI_WINBLUE) // NTDDI_WIN81
35 #define MAXIMUM_PAGEFILE_SIZE32 ((1ULL * 1024 * 1024 - 1) * PAGE_SIZE)
36 // PAGE_ROUND_DOWN(4ULL * GIGABYTE - 1)
39 #define MAXIMUM_PAGEFILE_SIZE32 (4095ULL * MEGABYTE)
42 // NOTE: No changes for NTDDI_WIN10
43 #if (NTDDI_VERSION >= NTDDI_WINBLUE) // NTDDI_WIN81
44 #define MAXIMUM_PAGEFILE_SIZE64 ((4ULL * 1024 * 1024 * 1024 - 1) * PAGE_SIZE)
45 // PAGE_ROUND_DOWN(16ULL * TERABYTE - 1)
48 #define MAXIMUM_PAGEFILE_SIZE64 (16ULL * TERABYTE)
52 #define MAXIMUM_PAGEFILE_SIZE MAXIMUM_PAGEFILE_SIZE32
53 /* PAE uses the same size as x64 */
54 #define MAXIMUM_PAGEFILE_SIZE_PAE MAXIMUM_PAGEFILE_SIZE64
55 #elif defined (_M_AMD64) || defined(_M_ARM64)
56 #define MAXIMUM_PAGEFILE_SIZE MAXIMUM_PAGEFILE_SIZE64
57 #elif defined (_M_IA64)
59 #define MAXIMUM_PAGEFILE_SIZE (32ULL * TERABYTE)
62 // NOTE: No changes for NTDDI_WIN10
63 #if (NTDDI_VERSION >= NTDDI_WINBLUE) // NTDDI_WIN81
64 #define MAXIMUM_PAGEFILE_SIZE ((512ULL * 1024 - 1) * PAGE_SIZE)
65 // PAGE_ROUND_DOWN(2ULL * GIGABYTE - 1)
68 #define MAXIMUM_PAGEFILE_SIZE MAXIMUM_PAGEFILE_SIZE32
71 /* On unknown architectures, default to either one of the 32 or 64 bit sizes */
72 #pragma message("Unknown architecture")
74 #define MAXIMUM_PAGEFILE_SIZE MAXIMUM_PAGEFILE_SIZE64
76 #define MAXIMUM_PAGEFILE_SIZE MAXIMUM_PAGEFILE_SIZE32
80 /* This should be 32 MB, but we need more than that for 2nd stage setup */
81 #define MINIMUM_TO_KEEP_FREE (256 * MEGABYTE)
82 #define FUZZ_FACTOR (16 * MEGABYTE)
85 // Structure and flags describing each pagefile
87 #define SMP_PAGEFILE_CREATED 0x01
88 #define SMP_PAGEFILE_DEFAULT 0x02
89 #define SMP_PAGEFILE_SYSTEM_MANAGED 0x04
90 #define SMP_PAGEFILE_WAS_TOO_BIG 0x08
91 #define SMP_PAGEFILE_ON_ANY_DRIVE 0x10
92 #define SMP_PAGEFILE_EMERGENCY 0x20
93 #define SMP_PAGEFILE_DUMP_PROCESSED 0x40
94 typedef struct _SMP_PAGEFILE_DESCRIPTOR
99 LARGE_INTEGER MinSize
;
100 LARGE_INTEGER MaxSize
;
101 LARGE_INTEGER ActualMinSize
;
102 LARGE_INTEGER ActualMaxSize
;
104 } SMP_PAGEFILE_DESCRIPTOR
, *PSMP_PAGEFILE_DESCRIPTOR
;
107 // Structure and flags describing each volume
109 #define SMP_VOLUME_INSERTED 0x01
110 #define SMP_VOLUME_PAGEFILE_CREATED 0x04
111 #define SMP_VOLUME_IS_BOOT 0x08
112 typedef struct _SMP_VOLUME_DESCRIPTOR
116 USHORT PageFileCount
;
118 LARGE_INTEGER FreeSpace
;
119 FILE_FS_DEVICE_INFORMATION DeviceInfo
;
120 } SMP_VOLUME_DESCRIPTOR
, *PSMP_VOLUME_DESCRIPTOR
;
122 LIST_ENTRY SmpPagingFileDescriptorList
, SmpVolumeDescriptorList
;
123 BOOLEAN SmpRegistrySpecifierPresent
;
124 ULONG SmpNumberOfPagingFiles
;
126 /* FUNCTIONS ******************************************************************/
130 SmpPagingFileInitialize(VOID
)
132 /* Initialize the two lists */
133 InitializeListHead(&SmpPagingFileDescriptorList
);
134 InitializeListHead(&SmpVolumeDescriptorList
);
139 SmpCreatePagingFileDescriptor(IN PUNICODE_STRING PageFileToken
)
142 ULONG MinSize
= 0, MaxSize
= 0;
143 BOOLEAN SystemManaged
= FALSE
, ZeroSize
= TRUE
;
144 PSMP_PAGEFILE_DESCRIPTOR Descriptor
, ListDescriptor
;
147 PLIST_ENTRY NextEntry
;
148 UNICODE_STRING PageFileName
, Arguments
, SecondArgument
;
150 /* Make sure we don't have too many */
151 if (SmpNumberOfPagingFiles
>= MAX_PAGING_FILES
)
153 DPRINT1("SMSS:PFILE: Too many paging files specified - %lu\n",
154 SmpNumberOfPagingFiles
);
155 return STATUS_TOO_MANY_PAGING_FILES
;
158 /* Parse the specified and get the name and arguments out of it */
159 DPRINT("SMSS:PFILE: Paging file specifier `%wZ'\n", PageFileToken
);
160 Status
= SmpParseCommandLine(PageFileToken
,
165 if (!NT_SUCCESS(Status
))
168 DPRINT1("SMSS:PFILE: SmpParseCommandLine(%wZ) failed - Status == %lx\n",
169 PageFileToken
, Status
);
173 /* Set the variable to let everyone know we have a pagefile token */
174 SmpRegistrySpecifierPresent
= TRUE
;
176 /* Parse the arguments, if any */
177 if (Arguments
.Buffer
)
179 /* Parse the pagefile size */
180 for (i
= 0; i
< Arguments
.Length
/ sizeof(WCHAR
); i
++)
182 /* Check if it's zero */
183 c
= Arguments
.Buffer
[i
];
184 if ((c
!= L
' ') && (c
!= L
'\t') && (c
!= L
'0'))
186 /* It isn't, break out */
193 /* Was a pagefile not specified, or was it specified with no size? */
194 if (!(Arguments
.Buffer
) || (ZeroSize
))
196 /* In this case, the system will manage its size */
197 SystemManaged
= TRUE
;
201 /* We do have a size, so convert the arguments into a number */
202 Status
= RtlUnicodeStringToInteger(&Arguments
, 0, &MinSize
);
203 if (!NT_SUCCESS(Status
))
206 RtlFreeUnicodeString(&PageFileName
);
207 RtlFreeUnicodeString(&Arguments
);
211 /* Now advance to the next argument */
212 for (i
= 0; i
< Arguments
.Length
/ sizeof(WCHAR
); i
++)
214 /* Found a space -- second argument must start here */
215 if (Arguments
.Buffer
[i
] == L
' ')
217 /* Use the rest of the arguments as a maximum size */
218 SecondArgument
.Buffer
= &Arguments
.Buffer
[i
];
219 SecondArgument
.Length
= (USHORT
)(Arguments
.Length
-
221 SecondArgument
.MaximumLength
= (USHORT
)(Arguments
.MaximumLength
-
223 Status
= RtlUnicodeStringToInteger(&SecondArgument
, 0, &MaxSize
);
224 if (!NT_SUCCESS(Status
))
227 RtlFreeUnicodeString(&PageFileName
);
228 RtlFreeUnicodeString(&Arguments
);
237 /* We are done parsing arguments */
238 RtlFreeUnicodeString(&Arguments
);
240 /* Now we can allocate our descriptor */
241 Descriptor
= RtlAllocateHeap(RtlGetProcessHeap(),
243 sizeof(SMP_PAGEFILE_DESCRIPTOR
));
246 /* Fail if we couldn't */
247 RtlFreeUnicodeString(&PageFileName
);
248 return STATUS_NO_MEMORY
;
251 /* Capture all our data into the descriptor */
252 Descriptor
->Token
= *PageFileToken
;
253 Descriptor
->Name
= PageFileName
;
254 Descriptor
->MinSize
.QuadPart
= MinSize
* MEGABYTE
;
255 Descriptor
->MaxSize
.QuadPart
= MaxSize
* MEGABYTE
;
257 Descriptor
->Flags
|= SMP_PAGEFILE_SYSTEM_MANAGED
;
258 Descriptor
->Name
.Buffer
[STANDARD_DRIVE_LETTER_OFFSET
] =
259 RtlUpcaseUnicodeChar(Descriptor
->Name
.Buffer
[STANDARD_DRIVE_LETTER_OFFSET
]);
260 if (Descriptor
->Name
.Buffer
[STANDARD_DRIVE_LETTER_OFFSET
] == '?')
262 Descriptor
->Flags
|= SMP_PAGEFILE_ON_ANY_DRIVE
;
265 /* Now loop the existing descriptors */
266 NextEntry
= SmpPagingFileDescriptorList
.Flink
;
269 /* Are there none, or have we looped back to the beginning? */
270 if (NextEntry
== &SmpPagingFileDescriptorList
)
272 /* This means no duplicates exist, so insert our descriptor! */
273 InsertTailList(&SmpPagingFileDescriptorList
, &Descriptor
->Entry
);
274 SmpNumberOfPagingFiles
++;
275 DPRINT("SMSS:PFILE: Created descriptor for `%wZ' (`%wZ')\n",
276 PageFileToken
, &Descriptor
->Name
);
277 return STATUS_SUCCESS
;
280 /* Keep going until we find a duplicate, unless we are in "any" mode */
281 ListDescriptor
= CONTAINING_RECORD(NextEntry
, SMP_PAGEFILE_DESCRIPTOR
, Entry
);
282 NextEntry
= NextEntry
->Flink
;
283 } while (!(ListDescriptor
->Flags
& SMP_PAGEFILE_ON_ANY_DRIVE
) ||
284 !(Descriptor
->Flags
& SMP_PAGEFILE_ON_ANY_DRIVE
));
286 /* We found a duplicate, so skip this descriptor/pagefile and fail */
287 DPRINT1("SMSS:PFILE: Skipping duplicate specifier `%wZ'\n", PageFileToken
);
288 RtlFreeUnicodeString(&PageFileName
);
289 RtlFreeHeap(RtlGetProcessHeap(), 0, Descriptor
);
290 return STATUS_INVALID_PARAMETER
;
295 SmpGetPagingFileSize(IN PUNICODE_STRING FileName
,
296 OUT PLARGE_INTEGER Size
)
299 OBJECT_ATTRIBUTES ObjectAttributes
;
300 IO_STATUS_BLOCK IoStatusBlock
;
302 FILE_STANDARD_INFORMATION StandardInfo
;
304 DPRINT("SMSS:PFILE: Trying to get size for `%wZ'\n", FileName
);
307 InitializeObjectAttributes(&ObjectAttributes
,
309 OBJ_CASE_INSENSITIVE
,
312 Status
= NtOpenFile(&FileHandle
,
313 FILE_READ_ATTRIBUTES
| SYNCHRONIZE
,
316 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
317 FILE_SYNCHRONOUS_IO_NONALERT
);
318 if (!NT_SUCCESS(Status
)) return Status
;
320 Status
= NtQueryInformationFile(FileHandle
,
323 sizeof(StandardInfo
),
324 FileStandardInformation
);
325 if (!NT_SUCCESS(Status
))
327 DPRINT1("SMSS:PFILE: Failed query for size potential pagefile `%wZ' with status %X\n",
334 Size
->QuadPart
= StandardInfo
.AllocationSize
.QuadPart
;
335 return STATUS_SUCCESS
;
340 SmpDeletePagingFile(IN PUNICODE_STRING FileName
)
343 OBJECT_ATTRIBUTES ObjectAttributes
;
344 IO_STATUS_BLOCK IoStatusBlock
;
346 FILE_DISPOSITION_INFORMATION Disposition
;
348 /* Open the page file */
349 InitializeObjectAttributes(&ObjectAttributes
,
351 OBJ_CASE_INSENSITIVE
,
354 Status
= NtOpenFile(&FileHandle
,
358 FILE_SHARE_DELETE
| FILE_SHARE_READ
| FILE_SHARE_WRITE
,
359 FILE_NON_DIRECTORY_FILE
);
360 if (NT_SUCCESS(Status
))
363 Disposition
.DeleteFile
= TRUE
;
364 Status
= NtSetInformationFile(FileHandle
,
368 FileDispositionInformation
);
369 if (!NT_SUCCESS(Status
))
371 DPRINT1("SMSS:PFILE: Failed to delete page file `%wZ' (status %X)\n",
376 DPRINT("SMSS:PFILE: Deleted stale paging file - %wZ\n", FileName
);
379 /* Close the handle */
384 DPRINT1("SMSS:PFILE: Failed to open for deletion page file `%wZ' (status %X)\n",
394 SmpGetVolumeFreeSpace(IN PSMP_VOLUME_DESCRIPTOR Volume
)
397 LARGE_INTEGER FreeSpace
, FinalFreeSpace
;
398 FILE_FS_SIZE_INFORMATION SizeInfo
;
399 IO_STATUS_BLOCK IoStatusBlock
;
400 OBJECT_ATTRIBUTES ObjectAttributes
;
401 UNICODE_STRING VolumeName
;
403 WCHAR PathString
[32];
404 ASSERT(Volume
->Flags
& SMP_VOLUME_IS_BOOT
); // ASSERT says "BootVolume == 1"
406 /* Build the standard path */
407 wcscpy(PathString
, L
"\\??\\A:\\");
408 RtlInitUnicodeString(&VolumeName
, PathString
);
409 VolumeName
.Buffer
[STANDARD_DRIVE_LETTER_OFFSET
] = Volume
->DriveLetter
;
410 DPRINT("SMSS:PFILE: Querying volume `%wZ' for free space\n", &VolumeName
);
412 /* Open the volume */
413 InitializeObjectAttributes(&ObjectAttributes
,
415 OBJ_CASE_INSENSITIVE
,
418 Status
= NtOpenFile(&VolumeHandle
,
419 FILE_READ_ATTRIBUTES
| FILE_WRITE_ATTRIBUTES
| SYNCHRONIZE
,
422 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
423 FILE_SYNCHRONOUS_IO_NONALERT
| FILE_DIRECTORY_FILE
);
424 if (!NT_SUCCESS(Status
))
426 DPRINT1("SMSS:PFILE: Open volume `%wZ' failed with status %X\n", &VolumeName
, Status
);
430 /* Now get size information on the volume */
431 Status
= NtQueryVolumeInformationFile(VolumeHandle
,
435 FileFsSizeInformation
);
436 if (!NT_SUCCESS(Status
))
439 DPRINT1("SMSS:PFILE: Query volume `%wZ' (handle %p) for size failed"
444 NtClose(VolumeHandle
);
447 NtClose(VolumeHandle
);
449 /* Compute how much free space we have */
450 FreeSpace
.QuadPart
= SizeInfo
.AvailableAllocationUnits
.QuadPart
*
451 SizeInfo
.SectorsPerAllocationUnit
;
452 FinalFreeSpace
.QuadPart
= FreeSpace
.QuadPart
* SizeInfo
.BytesPerSector
;
454 /* Check if there is less than 32 MB free so we don't starve the disk */
455 if (FinalFreeSpace
.QuadPart
<= MINIMUM_TO_KEEP_FREE
)
457 /* In this case, act as if there is no free space */
458 Volume
->FreeSpace
.QuadPart
= 0;
462 /* Trim off 32 MB to give the disk a bit of breathing room */
463 Volume
->FreeSpace
.QuadPart
= FinalFreeSpace
.QuadPart
-
464 MINIMUM_TO_KEEP_FREE
;
467 return STATUS_SUCCESS
;
470 PSMP_VOLUME_DESCRIPTOR
472 SmpSearchVolumeDescriptor(IN WCHAR DriveLetter
)
475 PSMP_VOLUME_DESCRIPTOR Volume
= NULL
;
476 PLIST_ENTRY NextEntry
;
478 /* Use upper case to reduce differences */
479 UpLetter
= RtlUpcaseUnicodeChar(DriveLetter
);
481 /* Loop each volume */
482 NextEntry
= SmpVolumeDescriptorList
.Flink
;
483 while (NextEntry
!= &SmpVolumeDescriptorList
)
486 Volume
= CONTAINING_RECORD(NextEntry
, SMP_VOLUME_DESCRIPTOR
, Entry
);
488 /* Make sure it's a valid entry with an uppcase drive letter */
489 ASSERT(Volume
->Flags
& SMP_VOLUME_INSERTED
); // Volume->Initialized in ASSERT
490 ASSERT(Volume
->DriveLetter
>= L
'A' && Volume
->DriveLetter
<= L
'Z');
492 /* Break if it matches, if not, keep going */
493 if (Volume
->DriveLetter
== UpLetter
) break;
494 NextEntry
= NextEntry
->Flink
;
497 /* Return the volume if one was found */
498 if (NextEntry
== &SmpVolumeDescriptorList
) Volume
= NULL
;
504 SmpCreatePagingFile(IN PUNICODE_STRING Name
,
505 IN PLARGE_INTEGER MinSize
,
506 IN PLARGE_INTEGER MaxSize
,
511 /* Tell the kernel to create the pagefile */
512 Status
= NtCreatePagingFile(Name
, MinSize
, MaxSize
, Priority
);
513 if (NT_SUCCESS(Status
))
515 DPRINT("SMSS:PFILE: NtCreatePagingFile(%wZ, 0x%I64X, 0x%I64X) succeeded\n",
522 DPRINT1("SMSS:PFILE: NtCreatePagingFile(%wZ, 0x%I64X, 0x%I64X) failed with %X\n",
529 /* Return the status */
535 SmpCreatePagingFileOnFixedDrive(IN PSMP_PAGEFILE_DESCRIPTOR Descriptor
,
536 IN PLARGE_INTEGER FuzzFactor
,
537 IN PLARGE_INTEGER MinimumSize
)
539 PSMP_VOLUME_DESCRIPTOR Volume
;
540 BOOLEAN ShouldDelete
;
542 LARGE_INTEGER PageFileSize
;
543 ASSERT(Descriptor
->Name
.Buffer
[STANDARD_DRIVE_LETTER_OFFSET
] != L
'?');
545 /* Try to find the volume descriptor for this drive letter */
546 ShouldDelete
= FALSE
;
547 Volume
= SmpSearchVolumeDescriptor(Descriptor
->Name
.Buffer
[STANDARD_DRIVE_LETTER_OFFSET
]);
550 /* Couldn't find it, fail */
551 DPRINT1("SMSS:PFILE: No volume descriptor for `%wZ'\n",
553 return STATUS_INVALID_PARAMETER
;
556 /* Check if this is the boot volume */
557 if (Volume
->Flags
& SMP_VOLUME_IS_BOOT
)
559 /* Check if we haven't yet processed a crash dump on this volume */
560 if (!(Descriptor
->Flags
& SMP_PAGEFILE_DUMP_PROCESSED
))
562 /* Try to find a crash dump and extract it */
563 DPRINT("SMSS:PFILE: Checking for crash dump in `%wZ' on boot volume\n",
565 SmpCheckForCrashDump(&Descriptor
->Name
);
567 /* Update how much free space we have now that we extracted a dump */
568 Status
= SmpGetVolumeFreeSpace(Volume
);
569 if (!NT_SUCCESS(Status
))
571 DPRINT1("SMSS:PFILE: Failed to query free space for boot volume `%wC'\n",
572 Volume
->DriveLetter
);
576 DPRINT("Queried free space for boot volume `%wC: 0x%I64x'\n",
577 Volume
->DriveLetter
, Volume
->FreeSpace
.QuadPart
);
580 /* Don't process crashdump on this volume anymore */
581 Descriptor
->Flags
|= SMP_PAGEFILE_DUMP_PROCESSED
;
586 /* Crashdumps can only be on the boot volume */
587 DPRINT("SMSS:PFILE: Skipping crash dump checking for `%wZ' on non boot"
590 Volume
->DriveLetter
);
593 /* Update the size after dump extraction */
594 Descriptor
->ActualMinSize
= Descriptor
->MinSize
;
595 Descriptor
->ActualMaxSize
= Descriptor
->MaxSize
;
597 /* Check how big we can make the pagefile */
598 Status
= SmpGetPagingFileSize(&Descriptor
->Name
, &PageFileSize
);
599 if (NT_SUCCESS(Status
) && PageFileSize
.QuadPart
> 0) ShouldDelete
= TRUE
;
600 DPRINT("SMSS:PFILE: Detected size 0x%I64X for future paging file `%wZ'\n",
603 DPRINT("SMSS:PFILE: Free space on volume `%wC' is 0x%I64X\n",
605 Volume
->FreeSpace
.QuadPart
);
607 /* Now update our size and make sure none of these are too big */
608 PageFileSize
.QuadPart
+= Volume
->FreeSpace
.QuadPart
;
609 if (Descriptor
->ActualMinSize
.QuadPart
> PageFileSize
.QuadPart
)
611 Descriptor
->ActualMinSize
= PageFileSize
;
613 if (Descriptor
->ActualMaxSize
.QuadPart
> PageFileSize
.QuadPart
)
615 Descriptor
->ActualMaxSize
= PageFileSize
;
617 DPRINT("SMSS:PFILE: min 0x%I64X, max 0x%I64X, real min 0x%I64X\n",
618 Descriptor
->ActualMinSize
.QuadPart
,
619 Descriptor
->ActualMaxSize
.QuadPart
,
620 MinimumSize
->QuadPart
);
622 /* Keep going until we've created a pagefile of the right size */
623 while (Descriptor
->ActualMinSize
.QuadPart
>= MinimumSize
->QuadPart
)
625 /* Call NT to do it */
626 Status
= SmpCreatePagingFile(&Descriptor
->Name
,
627 &Descriptor
->ActualMinSize
,
628 &Descriptor
->ActualMaxSize
,
630 if (NT_SUCCESS(Status
))
632 /* We're done, update flags and increase the count */
633 Descriptor
->Flags
|= SMP_PAGEFILE_CREATED
;
634 Volume
->Flags
|= SMP_VOLUME_PAGEFILE_CREATED
;
635 Volume
->PageFileCount
++;
639 /* We failed, try a slightly smaller pagefile */
640 Descriptor
->ActualMinSize
.QuadPart
-= FuzzFactor
->QuadPart
;
643 /* Check if we weren't able to create it */
644 if (Descriptor
->ActualMinSize
.QuadPart
< MinimumSize
->QuadPart
)
646 /* Delete the current page file and fail */
649 SmpDeletePagingFile(&Descriptor
->Name
);
651 /* FIXFIX: Windows Vista does this, and it seems like we should too, so try to see if this fixes KVM */
652 Volume
->FreeSpace
.QuadPart
= PageFileSize
.QuadPart
;
654 DPRINT1("SMSS:PFILE: Failing for min 0x%I64X, max 0x%I64X, real min 0x%I64X\n",
655 Descriptor
->ActualMinSize
.QuadPart
,
656 Descriptor
->ActualMaxSize
.QuadPart
,
657 MinimumSize
->QuadPart
);
658 Status
= STATUS_DISK_FULL
;
661 /* Return the status */
667 SmpCreatePagingFileOnAnyDrive(IN PSMP_PAGEFILE_DESCRIPTOR Descriptor
,
668 IN PLARGE_INTEGER FuzzFactor
,
669 IN PLARGE_INTEGER MinimumSize
)
671 PSMP_VOLUME_DESCRIPTOR Volume
;
672 NTSTATUS Status
= STATUS_DISK_FULL
;
673 PLIST_ENTRY NextEntry
;
674 ASSERT(Descriptor
->Name
.Buffer
[STANDARD_DRIVE_LETTER_OFFSET
] == L
'?');
676 /* Loop the volume list */
677 NextEntry
= SmpVolumeDescriptorList
.Flink
;
678 while (NextEntry
!= &SmpVolumeDescriptorList
)
681 Volume
= CONTAINING_RECORD(NextEntry
, SMP_VOLUME_DESCRIPTOR
, Entry
);
683 /* Make sure it's inserted and on a valid drive letter */
684 ASSERT(Volume
->Flags
& SMP_VOLUME_INSERTED
); // Volume->Initialized in ASSERT
685 ASSERT(Volume
->DriveLetter
>= L
'A' && Volume
->DriveLetter
<= L
'Z');
687 /* Write the drive letter to try creating it on this volume */
688 Descriptor
->Name
.Buffer
[STANDARD_DRIVE_LETTER_OFFSET
] = Volume
->DriveLetter
;
689 Status
= SmpCreatePagingFileOnFixedDrive(Descriptor
,
692 if (NT_SUCCESS(Status
)) break;
694 /* It didn't work, make it an any pagefile again and keep going */
695 Descriptor
->Name
.Buffer
[STANDARD_DRIVE_LETTER_OFFSET
] = L
'?';
696 NextEntry
= NextEntry
->Flink
;
699 /* Return disk full or success */
705 SmpMakeDefaultPagingFileDescriptor(IN PSMP_PAGEFILE_DESCRIPTOR Descriptor
)
707 /* The default descriptor uses 128 MB as a pagefile size */
708 Descriptor
->Flags
|= SMP_PAGEFILE_DEFAULT
;
709 Descriptor
->MinSize
.QuadPart
= 128 * MEGABYTE
;
710 Descriptor
->MaxSize
.QuadPart
= 128 * MEGABYTE
;
715 SmpMakeSystemManagedPagingFileDescriptor(IN PSMP_PAGEFILE_DESCRIPTOR Descriptor
)
718 ULONGLONG MinimumSize
, MaximumSize
, Ram
;
719 SYSTEM_BASIC_INFORMATION BasicInfo
;
721 /* Query the page size of the system, and the amount of RAM */
722 Status
= NtQuerySystemInformation(SystemBasicInformation
,
726 if (!NT_SUCCESS(Status
))
728 /* If we failed, use defaults since we have no idea otherwise */
729 DPRINT1("SMSS:PFILE: NtQuerySystemInformation failed with %x\n", Status
);
730 SmpMakeDefaultPagingFileDescriptor(Descriptor
);
734 /* Check how much RAM we have and set three times this amount as maximum */
735 Ram
= BasicInfo
.NumberOfPhysicalPages
* BasicInfo
.PageSize
;
736 MaximumSize
= 3 * Ram
;
738 /* If we have more than 1GB, use that as minimum, otherwise, use 1.5X RAM */
739 MinimumSize
= (Ram
>= 1024 * MEGABYTE
) ? Ram
: MaximumSize
/ 2;
741 /* Write the new sizes in the descriptor and mark it as system managed */
742 Descriptor
->MinSize
.QuadPart
= MinimumSize
;
743 Descriptor
->MaxSize
.QuadPart
= MaximumSize
;
744 Descriptor
->Flags
|= SMP_PAGEFILE_SYSTEM_MANAGED
;
749 SmpValidatePagingFileSizes(IN PSMP_PAGEFILE_DESCRIPTOR Descriptor
)
751 NTSTATUS Status
= STATUS_SUCCESS
;
752 BOOLEAN WasTooBig
= FALSE
;
753 ULONGLONG MinSize
, MaxSize
;
755 ULONGLONG MaxPageFileSize
=
756 (SharedUserData
->ProcessorFeatures
[PF_PAE_ENABLED
])
757 ? MAXIMUM_PAGEFILE_SIZE_PAE
: MAXIMUM_PAGEFILE_SIZE
;
759 static const ULONGLONG MaxPageFileSize
= MAXIMUM_PAGEFILE_SIZE
;
762 /* Capture the min and max */
763 MinSize
= Descriptor
->MinSize
.QuadPart
;
764 MaxSize
= Descriptor
->MaxSize
.QuadPart
;
766 DPRINT("SMSS:PFILE: Validating sizes for `%wZ' 0x%I64X 0x%I64X\n",
767 &Descriptor
->Name
, MinSize
, MaxSize
);
769 /* Don't let minimum be bigger than maximum */
770 if (MinSize
> MaxSize
)
773 /* Validate the minimum and maximum and trim them if they are too large */
774 if (MinSize
> MaxPageFileSize
)
777 MinSize
= MaxPageFileSize
;
779 if (MaxSize
> MaxPageFileSize
)
782 MaxSize
= MaxPageFileSize
;
785 /* If we trimmed, write a flag in the descriptor */
788 DPRINT("SMSS:PFILE: Trimmed size of `%wZ' to maximum allowed\n",
790 Descriptor
->Flags
|= SMP_PAGEFILE_WAS_TOO_BIG
;
793 /* Now write the (possibly trimmed) sizes back */
794 Descriptor
->MinSize
.QuadPart
= MinSize
;
795 Descriptor
->MaxSize
.QuadPart
= MaxSize
;
801 SmpCreateSystemManagedPagingFile(IN PSMP_PAGEFILE_DESCRIPTOR Descriptor
,
802 IN BOOLEAN DecreaseSize
)
804 LARGE_INTEGER FuzzFactor
, Size
;
806 /* Make sure there is at least 1 paging file and that we are system-managed */
807 ASSERT(SmpNumberOfPagingFiles
>= 1);
808 ASSERT(!IsListEmpty(&SmpPagingFileDescriptorList
));
809 ASSERT(Descriptor
->Flags
& SMP_PAGEFILE_SYSTEM_MANAGED
);
811 /* Keep decreasing the pagefile by this amount if we run out of space */
812 FuzzFactor
.QuadPart
= FUZZ_FACTOR
;
814 /* Create the descriptor for it (mainly the right sizes) and validate */
815 SmpMakeSystemManagedPagingFileDescriptor(Descriptor
);
816 SmpValidatePagingFileSizes(Descriptor
);
818 /* Use either the minimum size in the descriptor, or 16 MB in minimal mode */
819 Size
.QuadPart
= DecreaseSize
? 16 * MEGABYTE
: Descriptor
->MinSize
.QuadPart
;
821 /* Check if this should be a fixed pagefile or an any pagefile */
822 if (Descriptor
->Name
.Buffer
[STANDARD_DRIVE_LETTER_OFFSET
] == '?')
824 /* Find a disk for it */
825 return SmpCreatePagingFileOnAnyDrive(Descriptor
, &FuzzFactor
, &Size
);
828 /* Use the disk that was given */
829 return SmpCreatePagingFileOnFixedDrive(Descriptor
, &FuzzFactor
, &Size
);
834 SmpCreateEmergencyPagingFile(VOID
)
836 PSMP_PAGEFILE_DESCRIPTOR Descriptor
;
839 /* Allocate a descriptor */
840 Descriptor
= RtlAllocateHeap(RtlGetProcessHeap(),
842 sizeof(SMP_PAGEFILE_DESCRIPTOR
));
843 if (!Descriptor
) return STATUS_NO_MEMORY
;
846 RtlInitUnicodeString(&Descriptor
->Token
, NULL
);
848 /* Copy the default pagefile name */
849 ASSERT(sizeof(Buffer
) >= sizeof(STANDARD_PAGING_FILE_NAME
));
850 wcscpy(Buffer
, STANDARD_PAGING_FILE_NAME
);
852 /* Fill the rest of the descriptor out */
853 RtlInitUnicodeString(&Descriptor
->Name
, Buffer
);
854 Descriptor
->Name
.Buffer
[STANDARD_DRIVE_LETTER_OFFSET
] = '?';
855 Descriptor
->Flags
|= SMP_PAGEFILE_SYSTEM_MANAGED
|
856 SMP_PAGEFILE_EMERGENCY
|
857 SMP_PAGEFILE_ON_ANY_DRIVE
;
859 /* Insert it into the descriptor list */
860 InsertHeadList(&SmpPagingFileDescriptorList
, &Descriptor
->Entry
);
861 SmpNumberOfPagingFiles
++;
863 /* Go ahead and create it now, with the minimal size possible */
864 return SmpCreateSystemManagedPagingFile(Descriptor
, TRUE
);
869 SmpCreateVolumeDescriptors(VOID
)
872 UNICODE_STRING VolumePath
;
873 BOOLEAN BootVolumeFound
= FALSE
;
874 WCHAR StartChar
, Drive
, DriveDiff
;
876 OBJECT_ATTRIBUTES ObjectAttributes
;
877 IO_STATUS_BLOCK IoStatusBlock
;
878 PROCESS_DEVICEMAP_INFORMATION ProcessInformation
;
879 FILE_FS_DEVICE_INFORMATION DeviceInfo
;
880 FILE_FS_SIZE_INFORMATION SizeInfo
;
881 PSMP_VOLUME_DESCRIPTOR Volume
;
882 LARGE_INTEGER FreeSpace
, FinalFreeSpace
;
885 /* We should be starting with an empty list */
886 ASSERT(IsListEmpty(&SmpVolumeDescriptorList
));
888 /* Query the device map so we can get the drive letters */
889 Status
= NtQueryInformationProcess(NtCurrentProcess(),
891 &ProcessInformation
.Query
,
892 sizeof(ProcessInformation
.Query
),
894 if (!NT_SUCCESS(Status
))
896 DPRINT1("SMSS:PFILE: Query(ProcessDeviceMap) failed with status %X\n",
901 /* Build the volume string, starting with A: (we'll edit this in place) */
902 wcscpy(Buffer
, L
"\\??\\A:\\");
903 RtlInitUnicodeString(&VolumePath
, Buffer
);
905 /* Start with the C drive, except on NEC PC-98 */
906 StartChar
= IsNEC_98
? L
'A' : L
'C';
907 for (Drive
= StartChar
, DriveDiff
= StartChar
- L
'A'; Drive
<= L
'Z'; Drive
++, DriveDiff
++)
909 /* Skip the disk if it's not in the drive map */
910 if (!((1 << DriveDiff
) & ProcessInformation
.Query
.DriveMap
)) continue;
912 /* Write the drive letter and try to open the volume */
913 VolumePath
.Buffer
[STANDARD_DRIVE_LETTER_OFFSET
] = Drive
;
914 InitializeObjectAttributes(&ObjectAttributes
,
916 OBJ_CASE_INSENSITIVE
,
919 Status
= NtOpenFile(&VolumeHandle
,
920 FILE_READ_ATTRIBUTES
| FILE_WRITE_ATTRIBUTES
| SYNCHRONIZE
,
923 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
924 FILE_SYNCHRONOUS_IO_NONALERT
| FILE_DIRECTORY_FILE
);
925 if (!NT_SUCCESS(Status
))
927 /* Skip the volume if we failed */
928 DPRINT1("SMSS:PFILE: Open volume `%wZ' failed with status %X\n",
929 &VolumePath
, Status
);
933 /* Now query device information on the volume */
934 Status
= NtQueryVolumeInformationFile(VolumeHandle
,
938 FileFsDeviceInformation
);
939 if (!NT_SUCCESS(Status
))
941 /* Move to the next volume if we failed */
942 DPRINT1("SMSS:PFILE: Query volume `%wZ' (handle %p) for device info"
943 " failed with status %X\n",
947 NtClose(VolumeHandle
);
951 /* Check if this is a fixed disk */
952 if (DeviceInfo
.Characteristics
& (FILE_FLOPPY_DISKETTE
|
953 FILE_READ_ONLY_DEVICE
|
955 FILE_REMOVABLE_MEDIA
))
957 /* It isn't, so skip it */
958 DPRINT1("SMSS:PFILE: Volume `%wZ' (%X) cannot store a paging file\n",
960 DeviceInfo
.Characteristics
);
961 NtClose(VolumeHandle
);
965 /* We found a fixed volume, allocate a descriptor for it */
966 Volume
= RtlAllocateHeap(RtlGetProcessHeap(),
968 sizeof(SMP_VOLUME_DESCRIPTOR
));
971 /* Failed to allocate memory, try the next disk */
972 DPRINT1("SMSS:PFILE: Failed to allocate a volume descriptor (%u bytes)\n",
973 sizeof(SMP_VOLUME_DESCRIPTOR
));
974 NtClose(VolumeHandle
);
978 /* Save the drive letter and device information */
979 Volume
->DriveLetter
= Drive
;
980 Volume
->DeviceInfo
= DeviceInfo
;
982 /* Check if this is the boot volume */
983 if (RtlUpcaseUnicodeChar(Drive
) ==
984 RtlUpcaseUnicodeChar(SharedUserData
->NtSystemRoot
[0]))
987 ASSERT(BootVolumeFound
== FALSE
);
988 Volume
->Flags
|= SMP_VOLUME_IS_BOOT
;
989 BootVolumeFound
= TRUE
;
992 /* Now get size information on the volume */
993 Status
= NtQueryVolumeInformationFile(VolumeHandle
,
997 FileFsSizeInformation
);
998 if (!NT_SUCCESS(Status
))
1000 /* We failed -- keep going */
1001 DPRINT1("SMSS:PFILE: Query volume `%wZ' (handle %p) for size failed"
1002 " with status %X\n",
1006 RtlFreeHeap(RtlGetProcessHeap(), 0, Volume
);
1007 NtClose(VolumeHandle
);
1011 /* Done querying volume information, close the handle */
1012 NtClose(VolumeHandle
);
1014 /* Compute how much free space we have */
1015 FreeSpace
.QuadPart
= SizeInfo
.AvailableAllocationUnits
.QuadPart
*
1016 SizeInfo
.SectorsPerAllocationUnit
;
1017 FinalFreeSpace
.QuadPart
= FreeSpace
.QuadPart
* SizeInfo
.BytesPerSector
;
1019 /* Check if there is less than 32 MB free so we don't starve the disk */
1020 if (FinalFreeSpace
.QuadPart
<= MINIMUM_TO_KEEP_FREE
)
1022 /* In this case, act as if there is no free space */
1023 Volume
->FreeSpace
.QuadPart
= 0;
1027 /* Trim off 32 MB to give the disk a bit of breathing room */
1028 Volume
->FreeSpace
.QuadPart
= FinalFreeSpace
.QuadPart
-
1029 MINIMUM_TO_KEEP_FREE
;
1032 /* All done, add this volume to our descriptor list */
1033 InsertTailList(&SmpVolumeDescriptorList
, &Volume
->Entry
);
1034 Volume
->Flags
|= SMP_VOLUME_INSERTED
;
1035 DPRINT("SMSS:PFILE: Created volume descriptor for`%wZ'\n", &VolumePath
);
1038 /* We must've found at least the boot volume */
1039 ASSERT(BootVolumeFound
== TRUE
);
1040 ASSERT(!IsListEmpty(&SmpVolumeDescriptorList
));
1041 if (!IsListEmpty(&SmpVolumeDescriptorList
)) return STATUS_SUCCESS
;
1043 /* Something is really messed up if we found no disks at all */
1044 return STATUS_UNEXPECTED_IO_ERROR
;
1049 SmpCreatePagingFiles(VOID
)
1052 PSMP_PAGEFILE_DESCRIPTOR Descriptor
;
1053 LARGE_INTEGER Size
, FuzzFactor
;
1054 BOOLEAN Created
= FALSE
;
1055 PLIST_ENTRY NextEntry
;
1057 /* Check if no paging files were requested */
1058 if (!(SmpNumberOfPagingFiles
) && !(SmpRegistrySpecifierPresent
))
1060 /* The list should be empty -- nothing to do */
1061 ASSERT(IsListEmpty(&SmpPagingFileDescriptorList
));
1062 DPRINT1("SMSS:PFILE: No paging file was requested\n");
1063 return STATUS_SUCCESS
;
1066 /* Initialize the volume descriptors so we can know what's available */
1067 Status
= SmpCreateVolumeDescriptors();
1068 if (!NT_SUCCESS(Status
))
1070 /* We can't make decisions without this, so fail */
1071 DPRINT1("SMSS:PFILE: Failed to create volume descriptors (status %X)\n",
1076 /* If we fail creating pagefiles, try to reduce by this much each time */
1077 FuzzFactor
.QuadPart
= FUZZ_FACTOR
;
1079 /* Loop the descriptor list */
1080 NextEntry
= SmpPagingFileDescriptorList
.Flink
;
1081 while (NextEntry
!= &SmpPagingFileDescriptorList
)
1083 /* Check what kind of descriptor this is */
1084 Descriptor
= CONTAINING_RECORD(NextEntry
, SMP_PAGEFILE_DESCRIPTOR
, Entry
);
1085 if (Descriptor
->Flags
& SMP_PAGEFILE_SYSTEM_MANAGED
)
1087 /* This is a system-managed descriptor. Create the correct file */
1088 DPRINT("SMSS:PFILE: Creating a system managed paging file (`%wZ')\n",
1090 Status
= SmpCreateSystemManagedPagingFile(Descriptor
, FALSE
);
1091 if (!NT_SUCCESS(Status
))
1093 /* We failed -- try again, with size minimization this time */
1094 DPRINT("SMSS:PFILE: Trying lower sizes for (`%wZ')\n",
1096 Status
= SmpCreateSystemManagedPagingFile(Descriptor
, TRUE
);
1101 /* This is a manually entered descriptor. Validate its size first */
1102 SmpValidatePagingFileSizes(Descriptor
);
1104 /* Check if this is an ANY pagefile or a FIXED pagefile */
1105 DPRINT("SMSS:PFILE: Creating a normal paging file (`%wZ')\n",
1107 if (Descriptor
->Name
.Buffer
[STANDARD_DRIVE_LETTER_OFFSET
] == L
'?')
1109 /* It's an any pagefile, try to create it wherever possible */
1110 Size
= Descriptor
->MinSize
;
1111 Status
= SmpCreatePagingFileOnAnyDrive(Descriptor
,
1114 if (!NT_SUCCESS(Status
))
1116 /* We failed to create it. Try again with a smaller size */
1117 DPRINT("SMSS:PFILE: Trying lower sizes for (`%wZ')\n",
1119 Size
.QuadPart
= 16 * MEGABYTE
;
1120 Status
= SmpCreatePagingFileOnAnyDrive(Descriptor
,
1127 /* It's a fixed pagefile: override the minimum and use ours */
1128 Size
.QuadPart
= 16 * MEGABYTE
;
1129 Status
= SmpCreatePagingFileOnFixedDrive(Descriptor
,
1135 /* Go to the next descriptor */
1136 if (NT_SUCCESS(Status
)) Created
= TRUE
;
1137 NextEntry
= NextEntry
->Flink
;
1140 /* Check if none of the code in our loops above was able to create it */
1143 /* Build an emergency pagefile ourselves */
1144 DPRINT1("SMSS:PFILE: Creating emergency paging file.\n");
1145 Status
= SmpCreateEmergencyPagingFile();