2 * PROJECT: ReactOS Windows-Compatible Session Manager
3 * LICENSE: BSD 2-Clause License
4 * FILE: base/system/smss/smss.c
5 * PURPOSE: Main SMSS Code
6 * PROGRAMMERS: Alex Ionescu
9 /* INCLUDES *******************************************************************/
15 /* GLOBALS ********************************************************************/
20 #define STANDARD_PAGING_FILE_NAME L"\\??\\?:\\pagefile.sys"
21 #define STANDARD_DRIVE_LETTER_OFFSET 4
22 #define MEGABYTE 0x100000UL
23 #define MAXIMUM_PAGEFILE_SIZE (4095 * MEGABYTE)
24 /* This should be 32 MB, but we need more than that for 2nd stage setup */
25 #define MINIMUM_TO_KEEP_FREE (64 * MEGABYTE)
26 #define FUZZ_FACTOR (16 * MEGABYTE)
29 // Structure and flags describing each pagefile
31 #define SMP_PAGEFILE_CREATED 0x01
32 #define SMP_PAGEFILE_DEFAULT 0x02
33 #define SMP_PAGEFILE_SYSTEM_MANAGED 0x04
34 #define SMP_PAGEFILE_WAS_TOO_BIG 0x08
35 #define SMP_PAGEFILE_ON_ANY_DRIVE 0x10
36 #define SMP_PAGEFILE_EMERGENCY 0x20
37 #define SMP_PAGEFILE_DUMP_PROCESSED 0x40
38 typedef struct _SMP_PAGEFILE_DESCRIPTOR
43 LARGE_INTEGER MinSize
;
44 LARGE_INTEGER MaxSize
;
45 LARGE_INTEGER ActualMinSize
;
46 LARGE_INTEGER ActualMaxSize
;
48 } SMP_PAGEFILE_DESCRIPTOR
, *PSMP_PAGEFILE_DESCRIPTOR
;
51 // Structure and flags describing each volume
53 #define SMP_VOLUME_INSERTED 0x01
54 #define SMP_VOLUME_PAGEFILE_CREATED 0x04
55 #define SMP_VOLUME_IS_BOOT 0x08
56 typedef struct _SMP_VOLUME_DESCRIPTOR
62 LARGE_INTEGER FreeSpace
;
63 FILE_FS_DEVICE_INFORMATION DeviceInfo
;
64 } SMP_VOLUME_DESCRIPTOR
, *PSMP_VOLUME_DESCRIPTOR
;
66 LIST_ENTRY SmpPagingFileDescriptorList
, SmpVolumeDescriptorList
;
67 BOOLEAN SmpRegistrySpecifierPresent
;
68 ULONG SmpNumberOfPagingFiles
;
70 /* FUNCTIONS ******************************************************************/
74 SmpPagingFileInitialize(VOID
)
76 /* Initialize the two lists */
77 InitializeListHead(&SmpPagingFileDescriptorList
);
78 InitializeListHead(&SmpVolumeDescriptorList
);
83 SmpCreatePagingFileDescriptor(IN PUNICODE_STRING PageFileToken
)
86 ULONG MinSize
= 0, MaxSize
= 0;
87 BOOLEAN SystemManaged
= FALSE
, ZeroSize
= TRUE
;
88 PSMP_PAGEFILE_DESCRIPTOR Descriptor
, ListDescriptor
;
91 PLIST_ENTRY NextEntry
;
92 UNICODE_STRING PageFileName
, Arguments
, SecondArgument
;
94 /* Make sure we don't have too many */
95 if (SmpNumberOfPagingFiles
>= 16)
97 DPRINT1("SMSS:PFILE: Too many paging files specified - %lu\n",
98 SmpNumberOfPagingFiles
);
99 return STATUS_TOO_MANY_PAGING_FILES
;
102 /* Parse the specified and get the name and arguments out of it */
103 DPRINT("SMSS:PFILE: Paging file specifier `%wZ' \n", PageFileToken
);
104 Status
= SmpParseCommandLine(PageFileToken
,
109 if (!NT_SUCCESS(Status
))
112 DPRINT1("SMSS:PFILE: SmpParseCommandLine( %wZ ) failed - Status == %lx\n",
113 PageFileToken
, Status
);
117 /* Set the variable to let everyone know we have a pagefile token */
118 SmpRegistrySpecifierPresent
= TRUE
;
120 /* Parse the arguments, if any */
121 if (Arguments
.Buffer
)
123 /* Parse the pagefile size */
124 for (i
= 0; i
< Arguments
.Length
/ sizeof(WCHAR
); i
++)
126 /* Check if it's zero */
127 c
= Arguments
.Buffer
[i
];
128 if ((c
!= L
' ') && (c
!= L
'\t') && (c
!= L
'0'))
130 /* It isn't, break out */
137 /* Was a pagefile not specified, or was it specified with no size? */
138 if (!(Arguments
.Buffer
) || (ZeroSize
))
140 /* In this case, the system will manage its size */
141 SystemManaged
= TRUE
;
145 /* We do have a size, so convert the arguments into a number */
146 Status
= RtlUnicodeStringToInteger(&Arguments
, 0, &MinSize
);
147 if (!NT_SUCCESS(Status
))
150 RtlFreeUnicodeString(&PageFileName
);
151 RtlFreeUnicodeString(&Arguments
);
155 /* Now advance to the next argument */
156 for (i
= 0; i
< Arguments
.Length
/ sizeof(WCHAR
); i
++)
158 /* Found a space -- second argument must start here */
159 if (Arguments
.Buffer
[i
] == L
' ')
161 /* Use the rest of the arguments as a maximum size */
162 SecondArgument
.Buffer
= &Arguments
.Buffer
[i
];
163 SecondArgument
.Length
= (USHORT
)(Arguments
.Length
-
165 SecondArgument
.MaximumLength
= (USHORT
)(Arguments
.MaximumLength
-
167 Status
= RtlUnicodeStringToInteger(&SecondArgument
, 0, &MaxSize
);
168 if (!NT_SUCCESS(Status
))
171 RtlFreeUnicodeString(&PageFileName
);
172 RtlFreeUnicodeString(&Arguments
);
181 /* We are done parsing arguments */
182 RtlFreeUnicodeString(&Arguments
);
184 /* Now we can allocate our descriptor */
185 Descriptor
= RtlAllocateHeap(RtlGetProcessHeap(),
187 sizeof(SMP_PAGEFILE_DESCRIPTOR
));
190 /* Fail if we couldn't */
191 RtlFreeUnicodeString(&PageFileName
);
192 return STATUS_NO_MEMORY
;
195 /* Capture all our data into the descriptor */
196 Descriptor
->Token
= *PageFileToken
;
197 Descriptor
->Name
= PageFileName
;
198 Descriptor
->MinSize
.QuadPart
= MinSize
* MEGABYTE
;
199 Descriptor
->MaxSize
.QuadPart
= MaxSize
* MEGABYTE
;
200 if (SystemManaged
) Descriptor
->Flags
|= SMP_PAGEFILE_SYSTEM_MANAGED
;
201 Descriptor
->Name
.Buffer
[STANDARD_DRIVE_LETTER_OFFSET
] =
202 RtlUpcaseUnicodeChar(Descriptor
->Name
.Buffer
[STANDARD_DRIVE_LETTER_OFFSET
]);
203 if (Descriptor
->Name
.Buffer
[STANDARD_DRIVE_LETTER_OFFSET
] == '?')
205 Descriptor
->Flags
|= SMP_PAGEFILE_ON_ANY_DRIVE
;
208 /* Now loop the existing descriptors */
209 NextEntry
= SmpPagingFileDescriptorList
.Flink
;
212 /* Are there none, or have we looped back to the beginning? */
213 if (NextEntry
== &SmpPagingFileDescriptorList
)
215 /* This means no duplicates exist, so insert our descriptor! */
216 InsertTailList(&SmpPagingFileDescriptorList
, &Descriptor
->Entry
);
217 SmpNumberOfPagingFiles
++;
218 DPRINT("SMSS:PFILE: Created descriptor for `%wZ' (`%wZ') \n",
219 PageFileToken
, &Descriptor
->Name
);
220 return STATUS_SUCCESS
;
223 /* Keep going until we find a duplicate, unless we are in "any" mode */
224 ListDescriptor
= CONTAINING_RECORD(NextEntry
, SMP_PAGEFILE_DESCRIPTOR
, Entry
);
225 NextEntry
= NextEntry
->Flink
;
226 } while (!(ListDescriptor
->Flags
& SMP_PAGEFILE_ON_ANY_DRIVE
) ||
227 !(Descriptor
->Flags
& SMP_PAGEFILE_ON_ANY_DRIVE
));
229 /* We found a duplicate, so skip this descriptor/pagefile and fail */
230 DPRINT1("SMSS:PFILE: Skipping duplicate specifier `%wZ' \n", PageFileToken
);
231 RtlFreeUnicodeString(&PageFileName
);
232 RtlFreeHeap(RtlGetProcessHeap(), 0, Descriptor
);
233 return STATUS_INVALID_PARAMETER
;
238 SmpGetPagingFileSize(IN PUNICODE_STRING FileName
,
239 OUT PLARGE_INTEGER Size
)
242 OBJECT_ATTRIBUTES ObjectAttributes
;
243 IO_STATUS_BLOCK IoStatusBlock
;
245 FILE_STANDARD_INFORMATION StandardInfo
;
247 DPRINT("SMSS:PFILE: Trying to get size for `%wZ'\n", FileName
);
250 InitializeObjectAttributes(&ObjectAttributes
,
252 OBJ_CASE_INSENSITIVE
,
255 Status
= NtOpenFile(&FileHandle
,
256 FILE_READ_ATTRIBUTES
| SYNCHRONIZE
,
259 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
260 FILE_SYNCHRONOUS_IO_NONALERT
);
261 if (!NT_SUCCESS(Status
)) return Status
;
263 Status
= NtQueryInformationFile(FileHandle
,
266 sizeof(StandardInfo
),
267 FileStandardInformation
);
268 if (!NT_SUCCESS(Status
))
270 DPRINT1("SMSS:PFILE: Failed query for size potential pagefile `%wZ' with status %X \n",
277 Size
->QuadPart
= StandardInfo
.AllocationSize
.QuadPart
;
278 return STATUS_SUCCESS
;
283 SmpDeletePagingFile(IN PUNICODE_STRING FileName
)
286 OBJECT_ATTRIBUTES ObjectAttributes
;
287 IO_STATUS_BLOCK IoStatusBlock
;
289 FILE_DISPOSITION_INFORMATION Disposition
;
291 /* Open the page file */
292 InitializeObjectAttributes(&ObjectAttributes
,
294 OBJ_CASE_INSENSITIVE
,
297 Status
= NtOpenFile(&FileHandle
,
301 FILE_SHARE_DELETE
| FILE_SHARE_READ
| FILE_SHARE_WRITE
,
302 FILE_NON_DIRECTORY_FILE
);
303 if (NT_SUCCESS(Status
))
306 Disposition
.DeleteFile
= TRUE
;
307 Status
= NtSetInformationFile(FileHandle
,
311 FileDispositionInformation
);
312 if (!NT_SUCCESS(Status
))
314 DPRINT1("SMSS:PFILE: Failed to delete page file `%wZ' (status %X)\n",
319 DPRINT1("SMSS:PFILE: Deleted stale paging file - %wZ\n", FileName
);
322 /* Close the handle */
327 DPRINT1("SMSS:PFILE: Failed to open for deletion page file `%wZ' (status %X)\n",
337 SmpGetVolumeFreeSpace(IN PSMP_VOLUME_DESCRIPTOR Volume
)
340 LARGE_INTEGER FreeSpace
, FinalFreeSpace
;
341 FILE_FS_SIZE_INFORMATION SizeInfo
;
342 IO_STATUS_BLOCK IoStatusBlock
;
343 OBJECT_ATTRIBUTES ObjectAttributes
;
344 UNICODE_STRING VolumeName
;
346 WCHAR PathString
[32];
347 ASSERT(Volume
->Flags
& SMP_VOLUME_IS_BOOT
); // ASSERT says "BootVolume == 1"
349 /* Build the standard path */
350 wcscpy(PathString
, L
"\\??\\A:\\");
351 RtlInitUnicodeString(&VolumeName
, PathString
);
352 VolumeName
.Buffer
[STANDARD_DRIVE_LETTER_OFFSET
] = Volume
->DriveLetter
;
353 DPRINT("SMSS:PFILE: Querying volume `%wZ' for free space \n", &VolumeName
);
355 /* Open the volume */
356 InitializeObjectAttributes(&ObjectAttributes
,
358 OBJ_CASE_INSENSITIVE
,
361 Status
= NtOpenFile(&VolumeHandle
,
362 FILE_READ_ATTRIBUTES
| FILE_WRITE_ATTRIBUTES
| SYNCHRONIZE
,
365 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
366 FILE_SYNCHRONOUS_IO_NONALERT
| FILE_DIRECTORY_FILE
);
367 if (!NT_SUCCESS(Status
))
369 DPRINT1("SMSS:PFILE: Open volume `%wZ' failed with status %X \n", &VolumeName
, Status
);
373 /* Now get size information on the volume */
374 Status
= NtQueryVolumeInformationFile(VolumeHandle
,
378 FileFsSizeInformation
);
379 if (!NT_SUCCESS(Status
))
382 DPRINT1("SMSS:PFILE: Query volume `%wZ' (handle %p) for size failed"
383 " with status %X \n",
387 NtClose(VolumeHandle
);
390 NtClose(VolumeHandle
);
392 /* Compute how much free space we have */
393 FreeSpace
.QuadPart
= SizeInfo
.AvailableAllocationUnits
.QuadPart
*
394 SizeInfo
.SectorsPerAllocationUnit
;
395 FinalFreeSpace
.QuadPart
= FreeSpace
.QuadPart
* SizeInfo
.BytesPerSector
;
397 /* Check if there's less than 32MB free so we don't starve the disk */
398 if (FinalFreeSpace
.QuadPart
<= MINIMUM_TO_KEEP_FREE
)
400 /* In this case, act as if there's no free space */
401 Volume
->FreeSpace
.QuadPart
= 0;
405 /* Trim off 32MB to give the disk a bit of breathing room */
406 Volume
->FreeSpace
.QuadPart
= FinalFreeSpace
.QuadPart
-
407 MINIMUM_TO_KEEP_FREE
;
410 return STATUS_SUCCESS
;
413 PSMP_VOLUME_DESCRIPTOR
415 SmpSearchVolumeDescriptor(IN WCHAR DriveLetter
)
418 PSMP_VOLUME_DESCRIPTOR Volume
= NULL
;
419 PLIST_ENTRY NextEntry
;
421 /* Use upper case to reduce differences */
422 UpLetter
= RtlUpcaseUnicodeChar(DriveLetter
);
424 /* Loop each volume */
425 NextEntry
= SmpVolumeDescriptorList
.Flink
;
426 while (NextEntry
!= &SmpVolumeDescriptorList
)
429 Volume
= CONTAINING_RECORD(NextEntry
, SMP_VOLUME_DESCRIPTOR
, Entry
);
431 /* Make sure it's a valid entry with an uppcase drive letter */
432 ASSERT(Volume
->Flags
& SMP_VOLUME_INSERTED
); // Volume->Initialized in ASSERT
433 ASSERT(Volume
->DriveLetter
>= L
'A' && Volume
->DriveLetter
<= L
'Z');
435 /* Break if it matches, if not, keep going */
436 if (Volume
->DriveLetter
== UpLetter
) break;
437 NextEntry
= NextEntry
->Flink
;
440 /* Return the volume if one was found */
441 if (NextEntry
== &SmpVolumeDescriptorList
) Volume
= NULL
;
447 SmpCreatePagingFile(IN PUNICODE_STRING Name
,
448 IN PLARGE_INTEGER MinSize
,
449 IN PLARGE_INTEGER MaxSize
,
454 /* Tell the kernel to create the pagefile */
455 Status
= NtCreatePagingFile(Name
, MinSize
, MaxSize
, Priority
);
456 if (NT_SUCCESS(Status
))
458 DPRINT("SMSS:PFILE: NtCreatePagingFile (%wZ, %I64X, %I64X) succeeded. \n",
465 DPRINT1("SMSS:PFILE: NtCreatePagingFile (%wZ, %I64X, %I64X) failed with %X \n",
472 /* Return the status */
478 SmpCreatePagingFileOnFixedDrive(IN PSMP_PAGEFILE_DESCRIPTOR Descriptor
,
479 IN PLARGE_INTEGER FuzzFactor
,
480 IN PLARGE_INTEGER MinimumSize
)
482 PSMP_VOLUME_DESCRIPTOR Volume
;
483 BOOLEAN ShouldDelete
;
485 LARGE_INTEGER PageFileSize
;
486 ASSERT(Descriptor
->Name
.Buffer
[STANDARD_DRIVE_LETTER_OFFSET
] != L
'?');
488 /* Try to find the volume descriptor for this drive letter */
489 ShouldDelete
= FALSE
;
490 Volume
= SmpSearchVolumeDescriptor(Descriptor
->Name
.Buffer
[STANDARD_DRIVE_LETTER_OFFSET
]);
493 /* Couldn't find it, fail */
494 DPRINT1("SMSS:PFILE: No volume descriptor for `%wZ' \n",
496 return STATUS_INVALID_PARAMETER
;
499 /* Check if this is the boot volume */
500 if (Volume
->Flags
& SMP_VOLUME_IS_BOOT
)
502 /* Check if we haven't yet processed a crash dump on this volume */
503 if (!(Descriptor
->Flags
& SMP_PAGEFILE_DUMP_PROCESSED
))
505 /* Try to find a crash dump and extract it */
506 DPRINT("SMSS:PFILE: Checking for crash dump in `%wZ' on boot volume \n",
508 SmpCheckForCrashDump(&Descriptor
->Name
);
510 /* Update how much free space we have now that we extracted a dump */
511 Status
= SmpGetVolumeFreeSpace(Volume
);
512 if (!NT_SUCCESS(Status
))
514 DPRINT1("SMSS:PFILE: Failed to query free space for boot volume `%wC'\n",
515 Volume
->DriveLetter
);
519 DPRINT("Queried free space for boot volume `%wC: %I64x'\n",
520 Volume
->DriveLetter
, Volume
->FreeSpace
.QuadPart
);
523 /* Don't process crashdump on this volume anymore */
524 Descriptor
->Flags
|= SMP_PAGEFILE_DUMP_PROCESSED
;
529 /* Crashdumps can only be on the boot volume */
530 DPRINT("SMSS:PFILE: Skipping crash dump checking for `%wZ' on non boot"
533 Volume
->DriveLetter
);
536 /* Update the size after dump extraction */
537 Descriptor
->ActualMinSize
= Descriptor
->MinSize
;
538 Descriptor
->ActualMaxSize
= Descriptor
->MaxSize
;
540 /* Check how big we can make the pagefile */
541 Status
= SmpGetPagingFileSize(&Descriptor
->Name
, &PageFileSize
);
542 if (NT_SUCCESS(Status
) && PageFileSize
.QuadPart
> 0) ShouldDelete
= TRUE
;
543 DPRINT("SMSS:PFILE: Detected size %I64X for future paging file `%wZ'\n",
546 DPRINT("SMSS:PFILE: Free space on volume `%wC' is %I64X \n",
548 Volume
->FreeSpace
.QuadPart
);
550 /* Now update our size and make sure none of these are too big */
551 PageFileSize
.QuadPart
+= Volume
->FreeSpace
.QuadPart
;
552 if (Descriptor
->ActualMinSize
.QuadPart
> PageFileSize
.QuadPart
)
554 Descriptor
->ActualMinSize
= PageFileSize
;
556 if (Descriptor
->ActualMaxSize
.QuadPart
> PageFileSize
.QuadPart
)
558 Descriptor
->ActualMaxSize
= PageFileSize
;
560 DPRINT("SMSS:PFILE: min %I64X, max %I64X, real min %I64X \n",
561 Descriptor
->ActualMinSize
.QuadPart
,
562 Descriptor
->ActualMaxSize
.QuadPart
,
563 MinimumSize
->QuadPart
);
565 /* Keep going until we've created a pagefile of the right size */
566 while (Descriptor
->ActualMinSize
.QuadPart
>= MinimumSize
->QuadPart
)
568 /* Call NT to do it */
569 Status
= SmpCreatePagingFile(&Descriptor
->Name
,
570 &Descriptor
->ActualMinSize
,
571 &Descriptor
->ActualMaxSize
,
573 if (NT_SUCCESS(Status
))
575 /* We're done, update flags and increase the count */
576 Descriptor
->Flags
|= SMP_PAGEFILE_CREATED
;
577 Volume
->Flags
|= SMP_VOLUME_PAGEFILE_CREATED
;
578 Volume
->PageFileCount
++;
582 /* We failed, try a slighly smaller pagefile */
583 Descriptor
->ActualMinSize
.QuadPart
-= FuzzFactor
->QuadPart
;
586 /* Check if we weren't able to create it */
587 if (Descriptor
->ActualMinSize
.QuadPart
< MinimumSize
->QuadPart
)
589 /* Delete the current page file and fail */
592 SmpDeletePagingFile(&Descriptor
->Name
);
594 /* FIXFIX: Windows Vista does this, and it seems like we should too, so try to see if this fixes KVM */
595 Volume
->FreeSpace
.QuadPart
= PageFileSize
.QuadPart
;
597 DPRINT1("SMSS:PFILE: Failing for min %I64X, max %I64X, real min %I64X \n",
598 Descriptor
->ActualMinSize
.QuadPart
,
599 Descriptor
->ActualMaxSize
.QuadPart
,
600 MinimumSize
->QuadPart
);
601 Status
= STATUS_DISK_FULL
;
604 /* Return the status */
610 SmpCreatePagingFileOnAnyDrive(IN PSMP_PAGEFILE_DESCRIPTOR Descriptor
,
611 IN PLARGE_INTEGER FuzzFactor
,
612 IN PLARGE_INTEGER MinimumSize
)
614 PSMP_VOLUME_DESCRIPTOR Volume
;
615 NTSTATUS Status
= STATUS_DISK_FULL
;
616 PLIST_ENTRY NextEntry
;
617 ASSERT(Descriptor
->Name
.Buffer
[STANDARD_DRIVE_LETTER_OFFSET
] == L
'?');
619 /* Loop the volume list */
620 NextEntry
= SmpVolumeDescriptorList
.Flink
;
621 while (NextEntry
!= &SmpVolumeDescriptorList
)
624 Volume
= CONTAINING_RECORD(NextEntry
, SMP_VOLUME_DESCRIPTOR
, Entry
);
626 /* Make sure it's inserted and on a valid drive letter */
627 ASSERT(Volume
->Flags
& SMP_VOLUME_INSERTED
); // Volume->Initialized in ASSERT
628 ASSERT(Volume
->DriveLetter
>= L
'A' && Volume
->DriveLetter
<= L
'Z');
630 /* Write the drive letter to try creating it on this volume */
631 Descriptor
->Name
.Buffer
[STANDARD_DRIVE_LETTER_OFFSET
] = Volume
->DriveLetter
;
632 Status
= SmpCreatePagingFileOnFixedDrive(Descriptor
,
635 if (NT_SUCCESS(Status
)) break;
637 /* It didn't work, make it an any pagefile again and keep going */
638 Descriptor
->Name
.Buffer
[STANDARD_DRIVE_LETTER_OFFSET
] = L
'?';
639 NextEntry
= NextEntry
->Flink
;
642 /* Return disk full or success */
648 SmpMakeDefaultPagingFileDescriptor(IN PSMP_PAGEFILE_DESCRIPTOR Descriptor
)
650 /* The default descriptor uses 128MB as a pagefile size */
651 Descriptor
->Flags
|= SMP_PAGEFILE_DEFAULT
;
652 Descriptor
->MinSize
.QuadPart
= 128 * MEGABYTE
;
653 Descriptor
->MaxSize
.QuadPart
= 128 * MEGABYTE
;
658 SmpMakeSystemManagedPagingFileDescriptor(IN PSMP_PAGEFILE_DESCRIPTOR Descriptor
)
661 LONGLONG MinimumSize
, MaximumSize
, Ram
;
662 SYSTEM_BASIC_INFORMATION BasicInfo
;
664 /* Query the page size of the system, and the amount of RAM */
665 Status
= NtQuerySystemInformation(SystemBasicInformation
,
669 if (!NT_SUCCESS(Status
))
671 /* If we failed, use defaults since we have no idea otherwise */
672 DPRINT1("SMSS:PFILE: NtQuerySystemInformation failed with %x \n", Status
);
673 SmpMakeDefaultPagingFileDescriptor(Descriptor
);
677 /* Chekc how much RAM we have and set three times this amount as maximum */
678 Ram
= BasicInfo
.NumberOfPhysicalPages
* BasicInfo
.PageSize
;
679 MaximumSize
= 3 * Ram
;
681 /* If we have more than 1GB, use that as minimum, otherwise, use 1.5X RAM */
682 MinimumSize
= (Ram
>= 1024 * MEGABYTE
) ? Ram
: MaximumSize
/ 2;
684 /* Write the new sizes in the descriptor and mark it as system managed */
685 Descriptor
->MinSize
.QuadPart
= MinimumSize
;
686 Descriptor
->MaxSize
.QuadPart
= MaximumSize
;
687 Descriptor
->Flags
|= SMP_PAGEFILE_SYSTEM_MANAGED
;
692 SmpValidatePagingFileSizes(IN PSMP_PAGEFILE_DESCRIPTOR Descriptor
)
694 NTSTATUS Status
= STATUS_SUCCESS
;
695 ULONGLONG MinSize
, MaxSize
;
696 BOOLEAN WasTooBig
= FALSE
;
698 /* Capture the min and max */
699 MinSize
= Descriptor
->MinSize
.QuadPart
;
700 MaxSize
= Descriptor
->MaxSize
.QuadPart
;
701 DPRINT("SMSS:PFILE: Validating sizes for `%wZ' %I64X %I64X\n",
702 &Descriptor
->Name
, MinSize
, MaxSize
);
704 /* Don't let minimum be bigger than maximum */
705 if (MinSize
> MaxSize
) MaxSize
= MinSize
;
707 /* On PAE we can have bigger pagefiles... */
708 if (SharedUserData
->ProcessorFeatures
[PF_PAE_ENABLED
])
710 /* But we don't support that yet */
711 DPRINT1("ReactOS does not support PAE yet... assuming sizes OK\n");
715 /* Check if the minimum is more then 4095 MB */
716 if (MinSize
> MAXIMUM_PAGEFILE_SIZE
)
718 /* Trim it, this isn't allowed */
720 MinSize
= MAXIMUM_PAGEFILE_SIZE
;
723 /* Check if the maximum is more then 4095 MB */
724 if (MaxSize
> MAXIMUM_PAGEFILE_SIZE
)
726 /* Trim it, this isn't allowed */
728 MaxSize
= MAXIMUM_PAGEFILE_SIZE
;
735 /* Notify debugger output and write a flag in the descriptor */
736 DPRINT("SMSS:PFILE: Trimmed size of `%wZ' to maximum allowed \n",
738 Descriptor
->Flags
|= SMP_PAGEFILE_WAS_TOO_BIG
;
741 /* Now write the (possibly trimmed) sizes back */
742 Descriptor
->MinSize
.QuadPart
= MinSize
;
743 Descriptor
->MaxSize
.QuadPart
= MaxSize
;
749 SmpCreateSystemManagedPagingFile(IN PSMP_PAGEFILE_DESCRIPTOR Descriptor
,
750 IN BOOLEAN DecreaseSize
)
752 LARGE_INTEGER FuzzFactor
, Size
;
754 /* Make sure there's at least 1 paging file and that we are system-managed */
755 ASSERT(SmpNumberOfPagingFiles
>= 1);
756 ASSERT(!IsListEmpty(&SmpPagingFileDescriptorList
));
757 ASSERT(Descriptor
->Flags
& SMP_PAGEFILE_SYSTEM_MANAGED
); // Descriptor->SystemManaged == 1 in ASSERT.
759 /* Keep decreasing the pagefile by this amount if we run out of space */
760 FuzzFactor
.QuadPart
= FUZZ_FACTOR
;
762 /* Create the descriptor for it (mainly the right sizes) and validate */
763 SmpMakeSystemManagedPagingFileDescriptor(Descriptor
);
764 SmpValidatePagingFileSizes(Descriptor
);
766 /* Use either the minimum size in the descriptor, or 16MB in minimal mode */
767 Size
.QuadPart
= DecreaseSize
? 16 * MEGABYTE
: Descriptor
->MinSize
.QuadPart
;
769 /* Check if this should be a fixed pagefile or an any pagefile*/
770 if (Descriptor
->Name
.Buffer
[STANDARD_DRIVE_LETTER_OFFSET
] == '?')
772 /* Find a disk for it */
773 return SmpCreatePagingFileOnAnyDrive(Descriptor
, &FuzzFactor
, &Size
);
776 /* Use the disk that was given */
777 return SmpCreatePagingFileOnFixedDrive(Descriptor
, &FuzzFactor
, &Size
);
782 SmpCreateEmergencyPagingFile(VOID
)
784 PSMP_PAGEFILE_DESCRIPTOR Descriptor
;
787 /* Allocate a descriptor */
788 Descriptor
= RtlAllocateHeap(RtlGetProcessHeap(),
790 sizeof(SMP_PAGEFILE_DESCRIPTOR
));
791 if (!Descriptor
) return STATUS_NO_MEMORY
;
794 RtlInitUnicodeString(&Descriptor
->Token
, NULL
);
796 /* Copy the default pagefile name */
797 ASSERT(sizeof(Buffer
) >= sizeof(STANDARD_PAGING_FILE_NAME
));
798 wcscpy(Buffer
, STANDARD_PAGING_FILE_NAME
);
800 /* Fill the rest of the descriptor out */
801 RtlInitUnicodeString(&Descriptor
->Name
, Buffer
);
802 Descriptor
->Name
.Buffer
[STANDARD_DRIVE_LETTER_OFFSET
] = '?';
803 Descriptor
->Flags
|= SMP_PAGEFILE_SYSTEM_MANAGED
|
804 SMP_PAGEFILE_EMERGENCY
|
805 SMP_PAGEFILE_ON_ANY_DRIVE
;
807 /* Insert it into the descriptor list */
808 InsertHeadList(&SmpPagingFileDescriptorList
, &Descriptor
->Entry
);
809 SmpNumberOfPagingFiles
++;
811 /* Go ahead and create it now, with the minimal size possible */
812 return SmpCreateSystemManagedPagingFile(Descriptor
, TRUE
);
817 SmpCreateVolumeDescriptors(VOID
)
820 UNICODE_STRING VolumePath
;
821 BOOLEAN BootVolumeFound
= FALSE
;
822 WCHAR StartChar
, Drive
, DriveDiff
;
824 OBJECT_ATTRIBUTES ObjectAttributes
;
825 IO_STATUS_BLOCK IoStatusBlock
;
826 PROCESS_DEVICEMAP_INFORMATION ProcessInformation
;
827 FILE_FS_DEVICE_INFORMATION DeviceInfo
;
828 FILE_FS_SIZE_INFORMATION SizeInfo
;
829 PSMP_VOLUME_DESCRIPTOR Volume
;
830 LARGE_INTEGER FreeSpace
, FinalFreeSpace
;
833 /* We should be starting with an empty list */
834 ASSERT(IsListEmpty(&SmpVolumeDescriptorList
));
836 /* Query the device map so we can get the drive letters */
837 Status
= NtQueryInformationProcess(NtCurrentProcess(),
840 sizeof(ProcessInformation
),
842 if (!NT_SUCCESS(Status
))
844 DPRINT1("SMSS:PFILE: Query(ProcessDeviceMap) failed with status %X \n",
849 /* Build the volume string, starting with A: (we'll edit this in place) */
850 wcscpy(Buffer
, L
"\\??\\A:\\");
851 RtlInitUnicodeString(&VolumePath
, Buffer
);
853 /* Start with the C drive except on weird Japanese NECs... */
854 StartChar
= SharedUserData
->AlternativeArchitecture
? L
'A' : L
'C';
855 for (Drive
= StartChar
, DriveDiff
= StartChar
- L
'A'; Drive
<= L
'Z'; Drive
++, DriveDiff
++)
857 /* Skip the disk if it's not in the drive map */
858 if (!((1 << DriveDiff
) & ProcessInformation
.Query
.DriveMap
)) continue;
860 /* Write the drive letter and try to open the volume */
861 VolumePath
.Buffer
[STANDARD_DRIVE_LETTER_OFFSET
] = Drive
;
862 InitializeObjectAttributes(&ObjectAttributes
,
864 OBJ_CASE_INSENSITIVE
,
867 Status
= NtOpenFile(&VolumeHandle
,
868 FILE_READ_ATTRIBUTES
| FILE_WRITE_ATTRIBUTES
| SYNCHRONIZE
,
871 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
872 FILE_SYNCHRONOUS_IO_NONALERT
| FILE_DIRECTORY_FILE
);
873 if (!NT_SUCCESS(Status
))
875 /* Skip the volume if we failed */
876 DPRINT1("SMSS:PFILE: Open volume `%wZ' failed with status %X \n",
877 &VolumePath
, Status
);
881 /* Now query device information on the volume */
882 Status
= NtQueryVolumeInformationFile(VolumeHandle
,
886 FileFsDeviceInformation
);
887 if (!NT_SUCCESS(Status
))
889 /* Move to the next volume if we failed */
890 DPRINT1("SMSS:PFILE: Query volume `%wZ' (handle %p) for device info"
891 " failed with status %X \n",
895 NtClose(VolumeHandle
);
899 /* Check if this is a fixed disk */
900 if (DeviceInfo
.Characteristics
& (FILE_FLOPPY_DISKETTE
|
901 FILE_READ_ONLY_DEVICE
|
903 FILE_REMOVABLE_MEDIA
))
905 /* It isn't, so skip it */
906 DPRINT1("SMSS:PFILE: Volume `%wZ' (%X) cannot store a paging file \n",
908 DeviceInfo
.Characteristics
);
909 NtClose(VolumeHandle
);
913 /* We found a fixed volume, allocate a descriptor for it */
914 Volume
= RtlAllocateHeap(RtlGetProcessHeap(),
916 sizeof(SMP_VOLUME_DESCRIPTOR
));
919 /* Failed to allocate memory, try the next disk */
920 DPRINT1("SMSS:PFILE: Failed to allocate a volume descriptor (%u bytes) \n",
921 sizeof(SMP_VOLUME_DESCRIPTOR
));
922 NtClose(VolumeHandle
);
926 /* Save the drive letter and device information */
927 Volume
->DriveLetter
= Drive
;
928 Volume
->DeviceInfo
= DeviceInfo
;
930 /* Check if this is the boot volume */
931 if (RtlUpcaseUnicodeChar(Drive
) ==
932 RtlUpcaseUnicodeChar(SharedUserData
->NtSystemRoot
[0]))
935 ASSERT(BootVolumeFound
== FALSE
);
936 Volume
->Flags
|= SMP_VOLUME_IS_BOOT
;
937 BootVolumeFound
= TRUE
;
940 /* Now get size information on the volume */
941 Status
= NtQueryVolumeInformationFile(VolumeHandle
,
945 FileFsSizeInformation
);
946 if (!NT_SUCCESS(Status
))
948 /* We failed -- keep going */
949 DPRINT1("SMSS:PFILE: Query volume `%wZ' (handle %p) for size failed"
950 " with status %X \n",
954 RtlFreeHeap(RtlGetProcessHeap(), 0, Volume
);
955 NtClose(VolumeHandle
);
959 /* Done querying volume information, close the handle */
960 NtClose(VolumeHandle
);
962 /* Compute how much free space we have */
963 FreeSpace
.QuadPart
= SizeInfo
.AvailableAllocationUnits
.QuadPart
*
964 SizeInfo
.SectorsPerAllocationUnit
;
965 FinalFreeSpace
.QuadPart
= FreeSpace
.QuadPart
* SizeInfo
.BytesPerSector
;
967 /* Check if there's less than 32MB free so we don't starve the disk */
968 if (FinalFreeSpace
.QuadPart
<= MINIMUM_TO_KEEP_FREE
)
970 /* In this case, act as if there's no free space */
971 Volume
->FreeSpace
.QuadPart
= 0;
975 /* Trim off 32MB to give the disk a bit of breathing room */
976 Volume
->FreeSpace
.QuadPart
= FinalFreeSpace
.QuadPart
-
977 MINIMUM_TO_KEEP_FREE
;
980 /* All done, add this volume to our descriptor list */
981 InsertTailList(&SmpVolumeDescriptorList
, &Volume
->Entry
);
982 Volume
->Flags
|= SMP_VOLUME_INSERTED
;
983 DPRINT("SMSS:PFILE: Created volume descriptor for`%wZ' \n", &VolumePath
);
986 /* We must've found at least the boot volume */
987 ASSERT(BootVolumeFound
== TRUE
);
988 ASSERT(!IsListEmpty(&SmpVolumeDescriptorList
));
989 if (!IsListEmpty(&SmpVolumeDescriptorList
)) return STATUS_SUCCESS
;
991 /* Something is really messed up if we found no disks at all */
992 return STATUS_UNEXPECTED_IO_ERROR
;
997 SmpCreatePagingFiles(VOID
)
1000 PSMP_PAGEFILE_DESCRIPTOR Descriptor
;
1001 LARGE_INTEGER Size
, FuzzFactor
;
1002 BOOLEAN Created
= FALSE
;
1003 PLIST_ENTRY NextEntry
;
1005 /* Check if no paging files were requested */
1006 if (!(SmpNumberOfPagingFiles
) && !(SmpRegistrySpecifierPresent
))
1008 /* The list should be empty -- nothign to do */
1009 ASSERT(IsListEmpty(&SmpPagingFileDescriptorList
));
1010 DPRINT1("SMSS:PFILE: No paging file was requested \n");
1011 return STATUS_SUCCESS
;
1014 /* Initialize the volume descriptors so we can know what's available */
1015 Status
= SmpCreateVolumeDescriptors();
1016 if (!NT_SUCCESS(Status
))
1018 /* We can't make decisions without this, so fail */
1019 DPRINT1("SMSS:PFILE: Failed to create volume descriptors (status %X)\n",
1024 /* If we fail creating pagefiles, try to reduce by this much each time */
1025 FuzzFactor
.QuadPart
= FUZZ_FACTOR
;
1027 /* Loop the descriptor list */
1028 NextEntry
= SmpPagingFileDescriptorList
.Flink
;
1029 while (NextEntry
!= &SmpPagingFileDescriptorList
)
1031 /* Check what kind of descriptor this is */
1032 Descriptor
= CONTAINING_RECORD(NextEntry
, SMP_PAGEFILE_DESCRIPTOR
, Entry
);
1033 if (Descriptor
->Flags
& SMP_PAGEFILE_SYSTEM_MANAGED
)
1035 /* This is a system-managed descriptor. Create the correct file */
1036 DPRINT("SMSS:PFILE: Creating a system managed paging file (`%wZ')\n",
1038 Status
= SmpCreateSystemManagedPagingFile(Descriptor
, FALSE
);
1039 if (!NT_SUCCESS(Status
))
1041 /* We failed -- try again, with size minimization this time */
1042 DPRINT1("SMSS:PFILE: Trying lower sizes for (`%wZ') \n",
1044 Status
= SmpCreateSystemManagedPagingFile(Descriptor
, TRUE
);
1049 /* This is a manually entered descriptor. Validate its size first */
1050 SmpValidatePagingFileSizes(Descriptor
);
1052 /* Check if this is an ANY pagefile or a FIXED pagefile */
1053 DPRINT("SMSS:PFILE: Creating a normal paging file (`%wZ') \n",
1055 if (Descriptor
->Name
.Buffer
[STANDARD_DRIVE_LETTER_OFFSET
] == L
'?')
1057 /* It's an any pagefile, try to create it wherever possible */
1058 Size
= Descriptor
->MinSize
;
1059 Status
= SmpCreatePagingFileOnAnyDrive(Descriptor
,
1062 if (!NT_SUCCESS(Status
))
1064 /* We failed to create it. Try again with a smaller size */
1065 DPRINT1("SMSS:PFILE: Trying lower sizes for (`%wZ') \n",
1067 Size
.QuadPart
= 16 * MEGABYTE
;
1068 Status
= SmpCreatePagingFileOnAnyDrive(Descriptor
,
1075 /* It's a fixed pagefile: override the minimum and use ours */
1076 Size
.QuadPart
= 16 * MEGABYTE
;
1077 Status
= SmpCreatePagingFileOnFixedDrive(Descriptor
,
1083 /* Go to the next descriptor */
1084 if (NT_SUCCESS(Status
)) Created
= TRUE
;
1085 NextEntry
= NextEntry
->Flink
;
1088 /* Check if none of the code in our loops above was able to create it */
1091 /* Build an emergency pagefile ourselves */
1092 DPRINT1("SMSS:PFILE: Creating emergency paging file. \n");
1093 Status
= SmpCreateEmergencyPagingFile();