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 MEGABYTE 0x100000UL
24 #define MAXIMUM_PAGEFILE_SIZE (4095 * MEGABYTE)
25 /* This should be 32 MB, but we need more than that for 2nd stage setup */
26 #define MINIMUM_TO_KEEP_FREE (64 * MEGABYTE)
27 #define FUZZ_FACTOR (16 * MEGABYTE)
30 // Structure and flags describing each pagefile
32 #define SMP_PAGEFILE_CREATED 0x01
33 #define SMP_PAGEFILE_DEFAULT 0x02
34 #define SMP_PAGEFILE_SYSTEM_MANAGED 0x04
35 #define SMP_PAGEFILE_WAS_TOO_BIG 0x08
36 #define SMP_PAGEFILE_ON_ANY_DRIVE 0x10
37 #define SMP_PAGEFILE_EMERGENCY 0x20
38 #define SMP_PAGEFILE_DUMP_PROCESSED 0x40
39 typedef struct _SMP_PAGEFILE_DESCRIPTOR
44 LARGE_INTEGER MinSize
;
45 LARGE_INTEGER MaxSize
;
46 LARGE_INTEGER ActualMinSize
;
47 LARGE_INTEGER ActualMaxSize
;
49 } SMP_PAGEFILE_DESCRIPTOR
, *PSMP_PAGEFILE_DESCRIPTOR
;
52 // Structure and flags describing each volume
54 #define SMP_VOLUME_INSERTED 0x01
55 #define SMP_VOLUME_PAGEFILE_CREATED 0x04
56 #define SMP_VOLUME_IS_BOOT 0x08
57 typedef struct _SMP_VOLUME_DESCRIPTOR
63 LARGE_INTEGER FreeSpace
;
64 FILE_FS_DEVICE_INFORMATION DeviceInfo
;
65 } SMP_VOLUME_DESCRIPTOR
, *PSMP_VOLUME_DESCRIPTOR
;
67 LIST_ENTRY SmpPagingFileDescriptorList
, SmpVolumeDescriptorList
;
68 BOOLEAN SmpRegistrySpecifierPresent
;
69 ULONG SmpNumberOfPagingFiles
;
71 /* FUNCTIONS ******************************************************************/
75 SmpPagingFileInitialize(VOID
)
77 /* Initialize the two lists */
78 InitializeListHead(&SmpPagingFileDescriptorList
);
79 InitializeListHead(&SmpVolumeDescriptorList
);
84 SmpCreatePagingFileDescriptor(IN PUNICODE_STRING PageFileToken
)
87 ULONG MinSize
= 0, MaxSize
= 0;
88 BOOLEAN SystemManaged
= FALSE
, ZeroSize
= TRUE
;
89 PSMP_PAGEFILE_DESCRIPTOR Descriptor
, ListDescriptor
;
92 PLIST_ENTRY NextEntry
;
93 UNICODE_STRING PageFileName
, Arguments
, SecondArgument
;
95 /* Make sure we don't have too many */
96 if (SmpNumberOfPagingFiles
>= 16)
98 DPRINT1("SMSS:PFILE: Too many paging files specified - %lu\n",
99 SmpNumberOfPagingFiles
);
100 return STATUS_TOO_MANY_PAGING_FILES
;
103 /* Parse the specified and get the name and arguments out of it */
104 DPRINT("SMSS:PFILE: Paging file specifier `%wZ'\n", PageFileToken
);
105 Status
= SmpParseCommandLine(PageFileToken
,
110 if (!NT_SUCCESS(Status
))
113 DPRINT1("SMSS:PFILE: SmpParseCommandLine( %wZ ) failed - Status == %lx\n",
114 PageFileToken
, Status
);
118 /* Set the variable to let everyone know we have a pagefile token */
119 SmpRegistrySpecifierPresent
= TRUE
;
121 /* Parse the arguments, if any */
122 if (Arguments
.Buffer
)
124 /* Parse the pagefile size */
125 for (i
= 0; i
< Arguments
.Length
/ sizeof(WCHAR
); i
++)
127 /* Check if it's zero */
128 c
= Arguments
.Buffer
[i
];
129 if ((c
!= L
' ') && (c
!= L
'\t') && (c
!= L
'0'))
131 /* It isn't, break out */
138 /* Was a pagefile not specified, or was it specified with no size? */
139 if (!(Arguments
.Buffer
) || (ZeroSize
))
141 /* In this case, the system will manage its size */
142 SystemManaged
= TRUE
;
146 /* We do have a size, so convert the arguments into a number */
147 Status
= RtlUnicodeStringToInteger(&Arguments
, 0, &MinSize
);
148 if (!NT_SUCCESS(Status
))
151 RtlFreeUnicodeString(&PageFileName
);
152 RtlFreeUnicodeString(&Arguments
);
156 /* Now advance to the next argument */
157 for (i
= 0; i
< Arguments
.Length
/ sizeof(WCHAR
); i
++)
159 /* Found a space -- second argument must start here */
160 if (Arguments
.Buffer
[i
] == L
' ')
162 /* Use the rest of the arguments as a maximum size */
163 SecondArgument
.Buffer
= &Arguments
.Buffer
[i
];
164 SecondArgument
.Length
= (USHORT
)(Arguments
.Length
-
166 SecondArgument
.MaximumLength
= (USHORT
)(Arguments
.MaximumLength
-
168 Status
= RtlUnicodeStringToInteger(&SecondArgument
, 0, &MaxSize
);
169 if (!NT_SUCCESS(Status
))
172 RtlFreeUnicodeString(&PageFileName
);
173 RtlFreeUnicodeString(&Arguments
);
182 /* We are done parsing arguments */
183 RtlFreeUnicodeString(&Arguments
);
185 /* Now we can allocate our descriptor */
186 Descriptor
= RtlAllocateHeap(RtlGetProcessHeap(),
188 sizeof(SMP_PAGEFILE_DESCRIPTOR
));
191 /* Fail if we couldn't */
192 RtlFreeUnicodeString(&PageFileName
);
193 return STATUS_NO_MEMORY
;
196 /* Capture all our data into the descriptor */
197 Descriptor
->Token
= *PageFileToken
;
198 Descriptor
->Name
= PageFileName
;
199 Descriptor
->MinSize
.QuadPart
= MinSize
* MEGABYTE
;
200 Descriptor
->MaxSize
.QuadPart
= MaxSize
* MEGABYTE
;
201 if (SystemManaged
) Descriptor
->Flags
|= SMP_PAGEFILE_SYSTEM_MANAGED
;
202 Descriptor
->Name
.Buffer
[STANDARD_DRIVE_LETTER_OFFSET
] =
203 RtlUpcaseUnicodeChar(Descriptor
->Name
.Buffer
[STANDARD_DRIVE_LETTER_OFFSET
]);
204 if (Descriptor
->Name
.Buffer
[STANDARD_DRIVE_LETTER_OFFSET
] == '?')
206 Descriptor
->Flags
|= SMP_PAGEFILE_ON_ANY_DRIVE
;
209 /* Now loop the existing descriptors */
210 NextEntry
= SmpPagingFileDescriptorList
.Flink
;
213 /* Are there none, or have we looped back to the beginning? */
214 if (NextEntry
== &SmpPagingFileDescriptorList
)
216 /* This means no duplicates exist, so insert our descriptor! */
217 InsertTailList(&SmpPagingFileDescriptorList
, &Descriptor
->Entry
);
218 SmpNumberOfPagingFiles
++;
219 DPRINT("SMSS:PFILE: Created descriptor for `%wZ' (`%wZ')\n",
220 PageFileToken
, &Descriptor
->Name
);
221 return STATUS_SUCCESS
;
224 /* Keep going until we find a duplicate, unless we are in "any" mode */
225 ListDescriptor
= CONTAINING_RECORD(NextEntry
, SMP_PAGEFILE_DESCRIPTOR
, Entry
);
226 NextEntry
= NextEntry
->Flink
;
227 } while (!(ListDescriptor
->Flags
& SMP_PAGEFILE_ON_ANY_DRIVE
) ||
228 !(Descriptor
->Flags
& SMP_PAGEFILE_ON_ANY_DRIVE
));
230 /* We found a duplicate, so skip this descriptor/pagefile and fail */
231 DPRINT1("SMSS:PFILE: Skipping duplicate specifier `%wZ'\n", PageFileToken
);
232 RtlFreeUnicodeString(&PageFileName
);
233 RtlFreeHeap(RtlGetProcessHeap(), 0, Descriptor
);
234 return STATUS_INVALID_PARAMETER
;
239 SmpGetPagingFileSize(IN PUNICODE_STRING FileName
,
240 OUT PLARGE_INTEGER Size
)
243 OBJECT_ATTRIBUTES ObjectAttributes
;
244 IO_STATUS_BLOCK IoStatusBlock
;
246 FILE_STANDARD_INFORMATION StandardInfo
;
248 DPRINT("SMSS:PFILE: Trying to get size for `%wZ'\n", FileName
);
251 InitializeObjectAttributes(&ObjectAttributes
,
253 OBJ_CASE_INSENSITIVE
,
256 Status
= NtOpenFile(&FileHandle
,
257 FILE_READ_ATTRIBUTES
| SYNCHRONIZE
,
260 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
261 FILE_SYNCHRONOUS_IO_NONALERT
);
262 if (!NT_SUCCESS(Status
)) return Status
;
264 Status
= NtQueryInformationFile(FileHandle
,
267 sizeof(StandardInfo
),
268 FileStandardInformation
);
269 if (!NT_SUCCESS(Status
))
271 DPRINT1("SMSS:PFILE: Failed query for size potential pagefile `%wZ' with status %X\n",
278 Size
->QuadPart
= StandardInfo
.AllocationSize
.QuadPart
;
279 return STATUS_SUCCESS
;
284 SmpDeletePagingFile(IN PUNICODE_STRING FileName
)
287 OBJECT_ATTRIBUTES ObjectAttributes
;
288 IO_STATUS_BLOCK IoStatusBlock
;
290 FILE_DISPOSITION_INFORMATION Disposition
;
292 /* Open the page file */
293 InitializeObjectAttributes(&ObjectAttributes
,
295 OBJ_CASE_INSENSITIVE
,
298 Status
= NtOpenFile(&FileHandle
,
302 FILE_SHARE_DELETE
| FILE_SHARE_READ
| FILE_SHARE_WRITE
,
303 FILE_NON_DIRECTORY_FILE
);
304 if (NT_SUCCESS(Status
))
307 Disposition
.DeleteFile
= TRUE
;
308 Status
= NtSetInformationFile(FileHandle
,
312 FileDispositionInformation
);
313 if (!NT_SUCCESS(Status
))
315 DPRINT1("SMSS:PFILE: Failed to delete page file `%wZ' (status %X)\n",
320 DPRINT("SMSS:PFILE: Deleted stale paging file - %wZ\n", FileName
);
323 /* Close the handle */
328 DPRINT1("SMSS:PFILE: Failed to open for deletion page file `%wZ' (status %X)\n",
338 SmpGetVolumeFreeSpace(IN PSMP_VOLUME_DESCRIPTOR Volume
)
341 LARGE_INTEGER FreeSpace
, FinalFreeSpace
;
342 FILE_FS_SIZE_INFORMATION SizeInfo
;
343 IO_STATUS_BLOCK IoStatusBlock
;
344 OBJECT_ATTRIBUTES ObjectAttributes
;
345 UNICODE_STRING VolumeName
;
347 WCHAR PathString
[32];
348 ASSERT(Volume
->Flags
& SMP_VOLUME_IS_BOOT
); // ASSERT says "BootVolume == 1"
350 /* Build the standard path */
351 wcscpy(PathString
, L
"\\??\\A:\\");
352 RtlInitUnicodeString(&VolumeName
, PathString
);
353 VolumeName
.Buffer
[STANDARD_DRIVE_LETTER_OFFSET
] = Volume
->DriveLetter
;
354 DPRINT("SMSS:PFILE: Querying volume `%wZ' for free space\n", &VolumeName
);
356 /* Open the volume */
357 InitializeObjectAttributes(&ObjectAttributes
,
359 OBJ_CASE_INSENSITIVE
,
362 Status
= NtOpenFile(&VolumeHandle
,
363 FILE_READ_ATTRIBUTES
| FILE_WRITE_ATTRIBUTES
| SYNCHRONIZE
,
366 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
367 FILE_SYNCHRONOUS_IO_NONALERT
| FILE_DIRECTORY_FILE
);
368 if (!NT_SUCCESS(Status
))
370 DPRINT1("SMSS:PFILE: Open volume `%wZ' failed with status %X\n", &VolumeName
, Status
);
374 /* Now get size information on the volume */
375 Status
= NtQueryVolumeInformationFile(VolumeHandle
,
379 FileFsSizeInformation
);
380 if (!NT_SUCCESS(Status
))
383 DPRINT1("SMSS:PFILE: Query volume `%wZ' (handle %p) for size failed"
388 NtClose(VolumeHandle
);
391 NtClose(VolumeHandle
);
393 /* Compute how much free space we have */
394 FreeSpace
.QuadPart
= SizeInfo
.AvailableAllocationUnits
.QuadPart
*
395 SizeInfo
.SectorsPerAllocationUnit
;
396 FinalFreeSpace
.QuadPart
= FreeSpace
.QuadPart
* SizeInfo
.BytesPerSector
;
398 /* Check if there's less than 32MB free so we don't starve the disk */
399 if (FinalFreeSpace
.QuadPart
<= MINIMUM_TO_KEEP_FREE
)
401 /* In this case, act as if there's no free space */
402 Volume
->FreeSpace
.QuadPart
= 0;
406 /* Trim off 32MB to give the disk a bit of breathing room */
407 Volume
->FreeSpace
.QuadPart
= FinalFreeSpace
.QuadPart
-
408 MINIMUM_TO_KEEP_FREE
;
411 return STATUS_SUCCESS
;
414 PSMP_VOLUME_DESCRIPTOR
416 SmpSearchVolumeDescriptor(IN WCHAR DriveLetter
)
419 PSMP_VOLUME_DESCRIPTOR Volume
= NULL
;
420 PLIST_ENTRY NextEntry
;
422 /* Use upper case to reduce differences */
423 UpLetter
= RtlUpcaseUnicodeChar(DriveLetter
);
425 /* Loop each volume */
426 NextEntry
= SmpVolumeDescriptorList
.Flink
;
427 while (NextEntry
!= &SmpVolumeDescriptorList
)
430 Volume
= CONTAINING_RECORD(NextEntry
, SMP_VOLUME_DESCRIPTOR
, Entry
);
432 /* Make sure it's a valid entry with an uppcase drive letter */
433 ASSERT(Volume
->Flags
& SMP_VOLUME_INSERTED
); // Volume->Initialized in ASSERT
434 ASSERT(Volume
->DriveLetter
>= L
'A' && Volume
->DriveLetter
<= L
'Z');
436 /* Break if it matches, if not, keep going */
437 if (Volume
->DriveLetter
== UpLetter
) break;
438 NextEntry
= NextEntry
->Flink
;
441 /* Return the volume if one was found */
442 if (NextEntry
== &SmpVolumeDescriptorList
) Volume
= NULL
;
448 SmpCreatePagingFile(IN PUNICODE_STRING Name
,
449 IN PLARGE_INTEGER MinSize
,
450 IN PLARGE_INTEGER MaxSize
,
455 /* Tell the kernel to create the pagefile */
456 Status
= NtCreatePagingFile(Name
, MinSize
, MaxSize
, Priority
);
457 if (NT_SUCCESS(Status
))
459 DPRINT("SMSS:PFILE: NtCreatePagingFile (%wZ, %I64X, %I64X) succeeded.\n",
466 DPRINT1("SMSS:PFILE: NtCreatePagingFile (%wZ, %I64X, %I64X) failed with %X\n",
473 /* Return the status */
479 SmpCreatePagingFileOnFixedDrive(IN PSMP_PAGEFILE_DESCRIPTOR Descriptor
,
480 IN PLARGE_INTEGER FuzzFactor
,
481 IN PLARGE_INTEGER MinimumSize
)
483 PSMP_VOLUME_DESCRIPTOR Volume
;
484 BOOLEAN ShouldDelete
;
486 LARGE_INTEGER PageFileSize
;
487 ASSERT(Descriptor
->Name
.Buffer
[STANDARD_DRIVE_LETTER_OFFSET
] != L
'?');
489 /* Try to find the volume descriptor for this drive letter */
490 ShouldDelete
= FALSE
;
491 Volume
= SmpSearchVolumeDescriptor(Descriptor
->Name
.Buffer
[STANDARD_DRIVE_LETTER_OFFSET
]);
494 /* Couldn't find it, fail */
495 DPRINT1("SMSS:PFILE: No volume descriptor for `%wZ'\n",
497 return STATUS_INVALID_PARAMETER
;
500 /* Check if this is the boot volume */
501 if (Volume
->Flags
& SMP_VOLUME_IS_BOOT
)
503 /* Check if we haven't yet processed a crash dump on this volume */
504 if (!(Descriptor
->Flags
& SMP_PAGEFILE_DUMP_PROCESSED
))
506 /* Try to find a crash dump and extract it */
507 DPRINT("SMSS:PFILE: Checking for crash dump in `%wZ' on boot volume\n",
509 SmpCheckForCrashDump(&Descriptor
->Name
);
511 /* Update how much free space we have now that we extracted a dump */
512 Status
= SmpGetVolumeFreeSpace(Volume
);
513 if (!NT_SUCCESS(Status
))
515 DPRINT1("SMSS:PFILE: Failed to query free space for boot volume `%wC'\n",
516 Volume
->DriveLetter
);
520 DPRINT("Queried free space for boot volume `%wC: %I64x'\n",
521 Volume
->DriveLetter
, Volume
->FreeSpace
.QuadPart
);
524 /* Don't process crashdump on this volume anymore */
525 Descriptor
->Flags
|= SMP_PAGEFILE_DUMP_PROCESSED
;
530 /* Crashdumps can only be on the boot volume */
531 DPRINT("SMSS:PFILE: Skipping crash dump checking for `%wZ' on non boot"
534 Volume
->DriveLetter
);
537 /* Update the size after dump extraction */
538 Descriptor
->ActualMinSize
= Descriptor
->MinSize
;
539 Descriptor
->ActualMaxSize
= Descriptor
->MaxSize
;
541 /* Check how big we can make the pagefile */
542 Status
= SmpGetPagingFileSize(&Descriptor
->Name
, &PageFileSize
);
543 if (NT_SUCCESS(Status
) && PageFileSize
.QuadPart
> 0) ShouldDelete
= TRUE
;
544 DPRINT("SMSS:PFILE: Detected size %I64X for future paging file `%wZ'\n",
547 DPRINT("SMSS:PFILE: Free space on volume `%wC' is %I64X\n",
549 Volume
->FreeSpace
.QuadPart
);
551 /* Now update our size and make sure none of these are too big */
552 PageFileSize
.QuadPart
+= Volume
->FreeSpace
.QuadPart
;
553 if (Descriptor
->ActualMinSize
.QuadPart
> PageFileSize
.QuadPart
)
555 Descriptor
->ActualMinSize
= PageFileSize
;
557 if (Descriptor
->ActualMaxSize
.QuadPart
> PageFileSize
.QuadPart
)
559 Descriptor
->ActualMaxSize
= PageFileSize
;
561 DPRINT("SMSS:PFILE: min %I64X, max %I64X, real min %I64X\n",
562 Descriptor
->ActualMinSize
.QuadPart
,
563 Descriptor
->ActualMaxSize
.QuadPart
,
564 MinimumSize
->QuadPart
);
566 /* Keep going until we've created a pagefile of the right size */
567 while (Descriptor
->ActualMinSize
.QuadPart
>= MinimumSize
->QuadPart
)
569 /* Call NT to do it */
570 Status
= SmpCreatePagingFile(&Descriptor
->Name
,
571 &Descriptor
->ActualMinSize
,
572 &Descriptor
->ActualMaxSize
,
574 if (NT_SUCCESS(Status
))
576 /* We're done, update flags and increase the count */
577 Descriptor
->Flags
|= SMP_PAGEFILE_CREATED
;
578 Volume
->Flags
|= SMP_VOLUME_PAGEFILE_CREATED
;
579 Volume
->PageFileCount
++;
583 /* We failed, try a slightly smaller pagefile */
584 Descriptor
->ActualMinSize
.QuadPart
-= FuzzFactor
->QuadPart
;
587 /* Check if we weren't able to create it */
588 if (Descriptor
->ActualMinSize
.QuadPart
< MinimumSize
->QuadPart
)
590 /* Delete the current page file and fail */
593 SmpDeletePagingFile(&Descriptor
->Name
);
595 /* FIXFIX: Windows Vista does this, and it seems like we should too, so try to see if this fixes KVM */
596 Volume
->FreeSpace
.QuadPart
= PageFileSize
.QuadPart
;
598 DPRINT1("SMSS:PFILE: Failing for min %I64X, max %I64X, real min %I64X\n",
599 Descriptor
->ActualMinSize
.QuadPart
,
600 Descriptor
->ActualMaxSize
.QuadPart
,
601 MinimumSize
->QuadPart
);
602 Status
= STATUS_DISK_FULL
;
605 /* Return the status */
611 SmpCreatePagingFileOnAnyDrive(IN PSMP_PAGEFILE_DESCRIPTOR Descriptor
,
612 IN PLARGE_INTEGER FuzzFactor
,
613 IN PLARGE_INTEGER MinimumSize
)
615 PSMP_VOLUME_DESCRIPTOR Volume
;
616 NTSTATUS Status
= STATUS_DISK_FULL
;
617 PLIST_ENTRY NextEntry
;
618 ASSERT(Descriptor
->Name
.Buffer
[STANDARD_DRIVE_LETTER_OFFSET
] == L
'?');
620 /* Loop the volume list */
621 NextEntry
= SmpVolumeDescriptorList
.Flink
;
622 while (NextEntry
!= &SmpVolumeDescriptorList
)
625 Volume
= CONTAINING_RECORD(NextEntry
, SMP_VOLUME_DESCRIPTOR
, Entry
);
627 /* Make sure it's inserted and on a valid drive letter */
628 ASSERT(Volume
->Flags
& SMP_VOLUME_INSERTED
); // Volume->Initialized in ASSERT
629 ASSERT(Volume
->DriveLetter
>= L
'A' && Volume
->DriveLetter
<= L
'Z');
631 /* Write the drive letter to try creating it on this volume */
632 Descriptor
->Name
.Buffer
[STANDARD_DRIVE_LETTER_OFFSET
] = Volume
->DriveLetter
;
633 Status
= SmpCreatePagingFileOnFixedDrive(Descriptor
,
636 if (NT_SUCCESS(Status
)) break;
638 /* It didn't work, make it an any pagefile again and keep going */
639 Descriptor
->Name
.Buffer
[STANDARD_DRIVE_LETTER_OFFSET
] = L
'?';
640 NextEntry
= NextEntry
->Flink
;
643 /* Return disk full or success */
649 SmpMakeDefaultPagingFileDescriptor(IN PSMP_PAGEFILE_DESCRIPTOR Descriptor
)
651 /* The default descriptor uses 128MB as a pagefile size */
652 Descriptor
->Flags
|= SMP_PAGEFILE_DEFAULT
;
653 Descriptor
->MinSize
.QuadPart
= 128 * MEGABYTE
;
654 Descriptor
->MaxSize
.QuadPart
= 128 * MEGABYTE
;
659 SmpMakeSystemManagedPagingFileDescriptor(IN PSMP_PAGEFILE_DESCRIPTOR Descriptor
)
662 LONGLONG MinimumSize
, MaximumSize
, Ram
;
663 SYSTEM_BASIC_INFORMATION BasicInfo
;
665 /* Query the page size of the system, and the amount of RAM */
666 Status
= NtQuerySystemInformation(SystemBasicInformation
,
670 if (!NT_SUCCESS(Status
))
672 /* If we failed, use defaults since we have no idea otherwise */
673 DPRINT1("SMSS:PFILE: NtQuerySystemInformation failed with %x\n", Status
);
674 SmpMakeDefaultPagingFileDescriptor(Descriptor
);
678 /* Chekc how much RAM we have and set three times this amount as maximum */
679 Ram
= BasicInfo
.NumberOfPhysicalPages
* BasicInfo
.PageSize
;
680 MaximumSize
= 3 * Ram
;
682 /* If we have more than 1GB, use that as minimum, otherwise, use 1.5X RAM */
683 MinimumSize
= (Ram
>= 1024 * MEGABYTE
) ? Ram
: MaximumSize
/ 2;
685 /* Write the new sizes in the descriptor and mark it as system managed */
686 Descriptor
->MinSize
.QuadPart
= MinimumSize
;
687 Descriptor
->MaxSize
.QuadPart
= MaximumSize
;
688 Descriptor
->Flags
|= SMP_PAGEFILE_SYSTEM_MANAGED
;
693 SmpValidatePagingFileSizes(IN PSMP_PAGEFILE_DESCRIPTOR Descriptor
)
695 NTSTATUS Status
= STATUS_SUCCESS
;
696 ULONGLONG MinSize
, MaxSize
;
697 BOOLEAN WasTooBig
= FALSE
;
699 /* Capture the min and max */
700 MinSize
= Descriptor
->MinSize
.QuadPart
;
701 MaxSize
= Descriptor
->MaxSize
.QuadPart
;
702 DPRINT("SMSS:PFILE: Validating sizes for `%wZ' %I64X %I64X\n",
703 &Descriptor
->Name
, MinSize
, MaxSize
);
705 /* Don't let minimum be bigger than maximum */
706 if (MinSize
> MaxSize
) MaxSize
= MinSize
;
708 /* On PAE we can have bigger pagefiles... */
709 if (SharedUserData
->ProcessorFeatures
[PF_PAE_ENABLED
])
711 /* But we don't support that yet */
712 DPRINT1("ReactOS does not support PAE yet... assuming sizes OK\n");
716 /* Check if the minimum is more then 4095 MB */
717 if (MinSize
> MAXIMUM_PAGEFILE_SIZE
)
719 /* Trim it, this isn't allowed */
721 MinSize
= MAXIMUM_PAGEFILE_SIZE
;
724 /* Check if the maximum is more then 4095 MB */
725 if (MaxSize
> MAXIMUM_PAGEFILE_SIZE
)
727 /* Trim it, this isn't allowed */
729 MaxSize
= MAXIMUM_PAGEFILE_SIZE
;
736 /* Notify debugger output and write a flag in the descriptor */
737 DPRINT("SMSS:PFILE: Trimmed size of `%wZ' to maximum allowed\n",
739 Descriptor
->Flags
|= SMP_PAGEFILE_WAS_TOO_BIG
;
742 /* Now write the (possibly trimmed) sizes back */
743 Descriptor
->MinSize
.QuadPart
= MinSize
;
744 Descriptor
->MaxSize
.QuadPart
= MaxSize
;
750 SmpCreateSystemManagedPagingFile(IN PSMP_PAGEFILE_DESCRIPTOR Descriptor
,
751 IN BOOLEAN DecreaseSize
)
753 LARGE_INTEGER FuzzFactor
, Size
;
755 /* Make sure there's at least 1 paging file and that we are system-managed */
756 ASSERT(SmpNumberOfPagingFiles
>= 1);
757 ASSERT(!IsListEmpty(&SmpPagingFileDescriptorList
));
758 ASSERT(Descriptor
->Flags
& SMP_PAGEFILE_SYSTEM_MANAGED
); // Descriptor->SystemManaged == 1 in ASSERT.
760 /* Keep decreasing the pagefile by this amount if we run out of space */
761 FuzzFactor
.QuadPart
= FUZZ_FACTOR
;
763 /* Create the descriptor for it (mainly the right sizes) and validate */
764 SmpMakeSystemManagedPagingFileDescriptor(Descriptor
);
765 SmpValidatePagingFileSizes(Descriptor
);
767 /* Use either the minimum size in the descriptor, or 16MB in minimal mode */
768 Size
.QuadPart
= DecreaseSize
? 16 * MEGABYTE
: Descriptor
->MinSize
.QuadPart
;
770 /* Check if this should be a fixed pagefile or an any pagefile*/
771 if (Descriptor
->Name
.Buffer
[STANDARD_DRIVE_LETTER_OFFSET
] == '?')
773 /* Find a disk for it */
774 return SmpCreatePagingFileOnAnyDrive(Descriptor
, &FuzzFactor
, &Size
);
777 /* Use the disk that was given */
778 return SmpCreatePagingFileOnFixedDrive(Descriptor
, &FuzzFactor
, &Size
);
783 SmpCreateEmergencyPagingFile(VOID
)
785 PSMP_PAGEFILE_DESCRIPTOR Descriptor
;
788 /* Allocate a descriptor */
789 Descriptor
= RtlAllocateHeap(RtlGetProcessHeap(),
791 sizeof(SMP_PAGEFILE_DESCRIPTOR
));
792 if (!Descriptor
) return STATUS_NO_MEMORY
;
795 RtlInitUnicodeString(&Descriptor
->Token
, NULL
);
797 /* Copy the default pagefile name */
798 ASSERT(sizeof(Buffer
) >= sizeof(STANDARD_PAGING_FILE_NAME
));
799 wcscpy(Buffer
, STANDARD_PAGING_FILE_NAME
);
801 /* Fill the rest of the descriptor out */
802 RtlInitUnicodeString(&Descriptor
->Name
, Buffer
);
803 Descriptor
->Name
.Buffer
[STANDARD_DRIVE_LETTER_OFFSET
] = '?';
804 Descriptor
->Flags
|= SMP_PAGEFILE_SYSTEM_MANAGED
|
805 SMP_PAGEFILE_EMERGENCY
|
806 SMP_PAGEFILE_ON_ANY_DRIVE
;
808 /* Insert it into the descriptor list */
809 InsertHeadList(&SmpPagingFileDescriptorList
, &Descriptor
->Entry
);
810 SmpNumberOfPagingFiles
++;
812 /* Go ahead and create it now, with the minimal size possible */
813 return SmpCreateSystemManagedPagingFile(Descriptor
, TRUE
);
818 SmpCreateVolumeDescriptors(VOID
)
821 UNICODE_STRING VolumePath
;
822 BOOLEAN BootVolumeFound
= FALSE
;
823 WCHAR StartChar
, Drive
, DriveDiff
;
825 OBJECT_ATTRIBUTES ObjectAttributes
;
826 IO_STATUS_BLOCK IoStatusBlock
;
827 PROCESS_DEVICEMAP_INFORMATION ProcessInformation
;
828 FILE_FS_DEVICE_INFORMATION DeviceInfo
;
829 FILE_FS_SIZE_INFORMATION SizeInfo
;
830 PSMP_VOLUME_DESCRIPTOR Volume
;
831 LARGE_INTEGER FreeSpace
, FinalFreeSpace
;
834 /* We should be starting with an empty list */
835 ASSERT(IsListEmpty(&SmpVolumeDescriptorList
));
837 /* Query the device map so we can get the drive letters */
838 Status
= NtQueryInformationProcess(NtCurrentProcess(),
841 sizeof(ProcessInformation
),
843 if (!NT_SUCCESS(Status
))
845 DPRINT1("SMSS:PFILE: Query(ProcessDeviceMap) failed with status %X\n",
850 /* Build the volume string, starting with A: (we'll edit this in place) */
851 wcscpy(Buffer
, L
"\\??\\A:\\");
852 RtlInitUnicodeString(&VolumePath
, Buffer
);
854 /* Start with the C drive except on weird Japanese NECs... */
855 StartChar
= SharedUserData
->AlternativeArchitecture
? L
'A' : L
'C';
856 for (Drive
= StartChar
, DriveDiff
= StartChar
- L
'A'; Drive
<= L
'Z'; Drive
++, DriveDiff
++)
858 /* Skip the disk if it's not in the drive map */
859 if (!((1 << DriveDiff
) & ProcessInformation
.Query
.DriveMap
)) continue;
861 /* Write the drive letter and try to open the volume */
862 VolumePath
.Buffer
[STANDARD_DRIVE_LETTER_OFFSET
] = Drive
;
863 InitializeObjectAttributes(&ObjectAttributes
,
865 OBJ_CASE_INSENSITIVE
,
868 Status
= NtOpenFile(&VolumeHandle
,
869 FILE_READ_ATTRIBUTES
| FILE_WRITE_ATTRIBUTES
| SYNCHRONIZE
,
872 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
873 FILE_SYNCHRONOUS_IO_NONALERT
| FILE_DIRECTORY_FILE
);
874 if (!NT_SUCCESS(Status
))
876 /* Skip the volume if we failed */
877 DPRINT1("SMSS:PFILE: Open volume `%wZ' failed with status %X\n",
878 &VolumePath
, Status
);
882 /* Now query device information on the volume */
883 Status
= NtQueryVolumeInformationFile(VolumeHandle
,
887 FileFsDeviceInformation
);
888 if (!NT_SUCCESS(Status
))
890 /* Move to the next volume if we failed */
891 DPRINT1("SMSS:PFILE: Query volume `%wZ' (handle %p) for device info"
892 " failed with status %X\n",
896 NtClose(VolumeHandle
);
900 /* Check if this is a fixed disk */
901 if (DeviceInfo
.Characteristics
& (FILE_FLOPPY_DISKETTE
|
902 FILE_READ_ONLY_DEVICE
|
904 FILE_REMOVABLE_MEDIA
))
906 /* It isn't, so skip it */
907 DPRINT1("SMSS:PFILE: Volume `%wZ' (%X) cannot store a paging file\n",
909 DeviceInfo
.Characteristics
);
910 NtClose(VolumeHandle
);
914 /* We found a fixed volume, allocate a descriptor for it */
915 Volume
= RtlAllocateHeap(RtlGetProcessHeap(),
917 sizeof(SMP_VOLUME_DESCRIPTOR
));
920 /* Failed to allocate memory, try the next disk */
921 DPRINT1("SMSS:PFILE: Failed to allocate a volume descriptor (%u bytes)\n",
922 sizeof(SMP_VOLUME_DESCRIPTOR
));
923 NtClose(VolumeHandle
);
927 /* Save the drive letter and device information */
928 Volume
->DriveLetter
= Drive
;
929 Volume
->DeviceInfo
= DeviceInfo
;
931 /* Check if this is the boot volume */
932 if (RtlUpcaseUnicodeChar(Drive
) ==
933 RtlUpcaseUnicodeChar(SharedUserData
->NtSystemRoot
[0]))
936 ASSERT(BootVolumeFound
== FALSE
);
937 Volume
->Flags
|= SMP_VOLUME_IS_BOOT
;
938 BootVolumeFound
= TRUE
;
941 /* Now get size information on the volume */
942 Status
= NtQueryVolumeInformationFile(VolumeHandle
,
946 FileFsSizeInformation
);
947 if (!NT_SUCCESS(Status
))
949 /* We failed -- keep going */
950 DPRINT1("SMSS:PFILE: Query volume `%wZ' (handle %p) for size failed"
955 RtlFreeHeap(RtlGetProcessHeap(), 0, Volume
);
956 NtClose(VolumeHandle
);
960 /* Done querying volume information, close the handle */
961 NtClose(VolumeHandle
);
963 /* Compute how much free space we have */
964 FreeSpace
.QuadPart
= SizeInfo
.AvailableAllocationUnits
.QuadPart
*
965 SizeInfo
.SectorsPerAllocationUnit
;
966 FinalFreeSpace
.QuadPart
= FreeSpace
.QuadPart
* SizeInfo
.BytesPerSector
;
968 /* Check if there's less than 32MB free so we don't starve the disk */
969 if (FinalFreeSpace
.QuadPart
<= MINIMUM_TO_KEEP_FREE
)
971 /* In this case, act as if there's no free space */
972 Volume
->FreeSpace
.QuadPart
= 0;
976 /* Trim off 32MB to give the disk a bit of breathing room */
977 Volume
->FreeSpace
.QuadPart
= FinalFreeSpace
.QuadPart
-
978 MINIMUM_TO_KEEP_FREE
;
981 /* All done, add this volume to our descriptor list */
982 InsertTailList(&SmpVolumeDescriptorList
, &Volume
->Entry
);
983 Volume
->Flags
|= SMP_VOLUME_INSERTED
;
984 DPRINT("SMSS:PFILE: Created volume descriptor for`%wZ'\n", &VolumePath
);
987 /* We must've found at least the boot volume */
988 ASSERT(BootVolumeFound
== TRUE
);
989 ASSERT(!IsListEmpty(&SmpVolumeDescriptorList
));
990 if (!IsListEmpty(&SmpVolumeDescriptorList
)) return STATUS_SUCCESS
;
992 /* Something is really messed up if we found no disks at all */
993 return STATUS_UNEXPECTED_IO_ERROR
;
998 SmpCreatePagingFiles(VOID
)
1001 PSMP_PAGEFILE_DESCRIPTOR Descriptor
;
1002 LARGE_INTEGER Size
, FuzzFactor
;
1003 BOOLEAN Created
= FALSE
;
1004 PLIST_ENTRY NextEntry
;
1006 /* Check if no paging files were requested */
1007 if (!(SmpNumberOfPagingFiles
) && !(SmpRegistrySpecifierPresent
))
1009 /* The list should be empty -- nothing to do */
1010 ASSERT(IsListEmpty(&SmpPagingFileDescriptorList
));
1011 DPRINT1("SMSS:PFILE: No paging file was requested\n");
1012 return STATUS_SUCCESS
;
1015 /* Initialize the volume descriptors so we can know what's available */
1016 Status
= SmpCreateVolumeDescriptors();
1017 if (!NT_SUCCESS(Status
))
1019 /* We can't make decisions without this, so fail */
1020 DPRINT1("SMSS:PFILE: Failed to create volume descriptors (status %X)\n",
1025 /* If we fail creating pagefiles, try to reduce by this much each time */
1026 FuzzFactor
.QuadPart
= FUZZ_FACTOR
;
1028 /* Loop the descriptor list */
1029 NextEntry
= SmpPagingFileDescriptorList
.Flink
;
1030 while (NextEntry
!= &SmpPagingFileDescriptorList
)
1032 /* Check what kind of descriptor this is */
1033 Descriptor
= CONTAINING_RECORD(NextEntry
, SMP_PAGEFILE_DESCRIPTOR
, Entry
);
1034 if (Descriptor
->Flags
& SMP_PAGEFILE_SYSTEM_MANAGED
)
1036 /* This is a system-managed descriptor. Create the correct file */
1037 DPRINT("SMSS:PFILE: Creating a system managed paging file (`%wZ')\n",
1039 Status
= SmpCreateSystemManagedPagingFile(Descriptor
, FALSE
);
1040 if (!NT_SUCCESS(Status
))
1042 /* We failed -- try again, with size minimization this time */
1043 DPRINT("SMSS:PFILE: Trying lower sizes for (`%wZ')\n",
1045 Status
= SmpCreateSystemManagedPagingFile(Descriptor
, TRUE
);
1050 /* This is a manually entered descriptor. Validate its size first */
1051 SmpValidatePagingFileSizes(Descriptor
);
1053 /* Check if this is an ANY pagefile or a FIXED pagefile */
1054 DPRINT("SMSS:PFILE: Creating a normal paging file (`%wZ')\n",
1056 if (Descriptor
->Name
.Buffer
[STANDARD_DRIVE_LETTER_OFFSET
] == L
'?')
1058 /* It's an any pagefile, try to create it wherever possible */
1059 Size
= Descriptor
->MinSize
;
1060 Status
= SmpCreatePagingFileOnAnyDrive(Descriptor
,
1063 if (!NT_SUCCESS(Status
))
1065 /* We failed to create it. Try again with a smaller size */
1066 DPRINT("SMSS:PFILE: Trying lower sizes for (`%wZ')\n",
1068 Size
.QuadPart
= 16 * MEGABYTE
;
1069 Status
= SmpCreatePagingFileOnAnyDrive(Descriptor
,
1076 /* It's a fixed pagefile: override the minimum and use ours */
1077 Size
.QuadPart
= 16 * MEGABYTE
;
1078 Status
= SmpCreatePagingFileOnFixedDrive(Descriptor
,
1084 /* Go to the next descriptor */
1085 if (NT_SUCCESS(Status
)) Created
= TRUE
;
1086 NextEntry
= NextEntry
->Flink
;
1089 /* Check if none of the code in our loops above was able to create it */
1092 /* Build an emergency pagefile ourselves */
1093 DPRINT1("SMSS:PFILE: Creating emergency paging file.\n");
1094 Status
= SmpCreateEmergencyPagingFile();