xref: /reactos/base/system/smss/pagefile.c (revision 34593d93)
1 /*
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
7  */
8 
9 /* INCLUDES *******************************************************************/
10 
11 #include "smss.h"
12 
13 #define NDEBUG
14 #include <debug.h>
15 
16 /* GLOBALS ********************************************************************/
17 
18 //
19 // Constants
20 //
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            (256 * MEGABYTE)
27 #define FUZZ_FACTOR                     (16 * MEGABYTE)
28 
29 //
30 // Structure and flags describing each pagefile
31 //
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
40 {
41     LIST_ENTRY Entry;
42     UNICODE_STRING Name;
43     UNICODE_STRING Token;
44     LARGE_INTEGER MinSize;
45     LARGE_INTEGER MaxSize;
46     LARGE_INTEGER ActualMinSize;
47     LARGE_INTEGER ActualMaxSize;
48     ULONG Flags;
49 } SMP_PAGEFILE_DESCRIPTOR, *PSMP_PAGEFILE_DESCRIPTOR;
50 
51 //
52 // Structure and flags describing each volume
53 //
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
58 {
59     LIST_ENTRY Entry;
60     USHORT Flags;
61     USHORT PageFileCount;
62     WCHAR DriveLetter;
63     LARGE_INTEGER FreeSpace;
64     FILE_FS_DEVICE_INFORMATION DeviceInfo;
65 } SMP_VOLUME_DESCRIPTOR, *PSMP_VOLUME_DESCRIPTOR;
66 
67 LIST_ENTRY SmpPagingFileDescriptorList, SmpVolumeDescriptorList;
68 BOOLEAN SmpRegistrySpecifierPresent;
69 ULONG SmpNumberOfPagingFiles;
70 
71 /* FUNCTIONS ******************************************************************/
72 
73 VOID
74 NTAPI
75 SmpPagingFileInitialize(VOID)
76 {
77     /* Initialize the two lists */
78     InitializeListHead(&SmpPagingFileDescriptorList);
79     InitializeListHead(&SmpVolumeDescriptorList);
80 }
81 
82 NTSTATUS
83 NTAPI
84 SmpCreatePagingFileDescriptor(IN PUNICODE_STRING PageFileToken)
85 {
86     NTSTATUS Status;
87     ULONG MinSize = 0, MaxSize = 0;
88     BOOLEAN SystemManaged = FALSE, ZeroSize = TRUE;
89     PSMP_PAGEFILE_DESCRIPTOR Descriptor, ListDescriptor;
90     ULONG i;
91     WCHAR c;
92     PLIST_ENTRY NextEntry;
93     UNICODE_STRING PageFileName, Arguments, SecondArgument;
94 
95     /* Make sure we don't have too many */
96     if (SmpNumberOfPagingFiles >= 16)
97     {
98         DPRINT1("SMSS:PFILE: Too many paging files specified - %lu\n",
99                 SmpNumberOfPagingFiles);
100         return STATUS_TOO_MANY_PAGING_FILES;
101     }
102 
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,
106                                  NULL,
107                                  &PageFileName,
108                                  NULL,
109                                  &Arguments);
110     if (!NT_SUCCESS(Status))
111     {
112         /* Fail */
113         DPRINT1("SMSS:PFILE: SmpParseCommandLine( %wZ ) failed - Status == %lx\n",
114                 PageFileToken, Status);
115         return Status;
116     }
117 
118     /* Set the variable to let everyone know we have a pagefile token */
119     SmpRegistrySpecifierPresent = TRUE;
120 
121     /* Parse the arguments, if any */
122     if (Arguments.Buffer)
123     {
124         /* Parse the pagefile size */
125         for (i = 0; i < Arguments.Length / sizeof(WCHAR); i++)
126         {
127             /* Check if it's zero */
128             c = Arguments.Buffer[i];
129             if ((c != L' ') && (c != L'\t') && (c != L'0'))
130             {
131                 /* It isn't, break out */
132                 ZeroSize = FALSE;
133                 break;
134             }
135         }
136     }
137 
138     /* Was a pagefile not specified, or was it specified with no size? */
139     if (!(Arguments.Buffer) || (ZeroSize))
140     {
141         /* In this case, the system will manage its size */
142         SystemManaged = TRUE;
143     }
144     else
145     {
146         /* We do have a size, so convert the arguments into a number */
147         Status = RtlUnicodeStringToInteger(&Arguments, 0, &MinSize);
148         if (!NT_SUCCESS(Status))
149         {
150             /* Fail */
151             RtlFreeUnicodeString(&PageFileName);
152             RtlFreeUnicodeString(&Arguments);
153             return Status;
154         }
155 
156         /* Now advance to the next argument */
157         for (i = 0; i < Arguments.Length / sizeof(WCHAR); i++)
158         {
159             /* Found a space -- second argument must start here */
160             if (Arguments.Buffer[i] == L' ')
161             {
162                 /* Use the rest of the arguments as a maximum size */
163                 SecondArgument.Buffer = &Arguments.Buffer[i];
164                 SecondArgument.Length = (USHORT)(Arguments.Length -
165                                         i * sizeof(WCHAR));
166                 SecondArgument.MaximumLength = (USHORT)(Arguments.MaximumLength -
167                                                i * sizeof(WCHAR));
168                 Status = RtlUnicodeStringToInteger(&SecondArgument, 0, &MaxSize);
169                 if (!NT_SUCCESS(Status))
170                 {
171                     /* Fail */
172                     RtlFreeUnicodeString(&PageFileName);
173                     RtlFreeUnicodeString(&Arguments);
174                     return Status;
175                 }
176 
177                 break;
178             }
179         }
180     }
181 
182     /* We are done parsing arguments */
183     RtlFreeUnicodeString(&Arguments);
184 
185     /* Now we can allocate our descriptor */
186     Descriptor = RtlAllocateHeap(RtlGetProcessHeap(),
187                                  HEAP_ZERO_MEMORY,
188                                  sizeof(SMP_PAGEFILE_DESCRIPTOR));
189     if (!Descriptor)
190     {
191         /* Fail if we couldn't */
192         RtlFreeUnicodeString(&PageFileName);
193         return STATUS_NO_MEMORY;
194     }
195 
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] == '?')
205     {
206         Descriptor->Flags |= SMP_PAGEFILE_ON_ANY_DRIVE;
207     }
208 
209     /* Now loop the existing descriptors */
210     NextEntry = SmpPagingFileDescriptorList.Flink;
211     do
212     {
213         /* Are there none, or have we looped back to the beginning? */
214         if (NextEntry == &SmpPagingFileDescriptorList)
215         {
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;
222         }
223 
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));
229 
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;
235 }
236 
237 NTSTATUS
238 NTAPI
239 SmpGetPagingFileSize(IN PUNICODE_STRING FileName,
240                      OUT PLARGE_INTEGER Size)
241 {
242     NTSTATUS Status;
243     OBJECT_ATTRIBUTES ObjectAttributes;
244     IO_STATUS_BLOCK IoStatusBlock;
245     HANDLE FileHandle;
246     FILE_STANDARD_INFORMATION StandardInfo;
247 
248     DPRINT("SMSS:PFILE: Trying to get size for `%wZ'\n", FileName);
249     Size->QuadPart = 0;
250 
251     InitializeObjectAttributes(&ObjectAttributes,
252                                FileName,
253                                OBJ_CASE_INSENSITIVE,
254                                NULL,
255                                NULL);
256     Status = NtOpenFile(&FileHandle,
257                         FILE_READ_ATTRIBUTES | SYNCHRONIZE,
258                         &ObjectAttributes,
259                         &IoStatusBlock,
260                         FILE_SHARE_READ | FILE_SHARE_WRITE,
261                         FILE_SYNCHRONOUS_IO_NONALERT);
262     if (!NT_SUCCESS(Status)) return Status;
263 
264     Status = NtQueryInformationFile(FileHandle,
265                                     &IoStatusBlock,
266                                     &StandardInfo,
267                                     sizeof(StandardInfo),
268                                     FileStandardInformation);
269     if (!NT_SUCCESS(Status))
270     {
271         DPRINT1("SMSS:PFILE: Failed query for size potential pagefile `%wZ' with status %X\n",
272                 FileName, Status);
273         NtClose(FileHandle);
274         return Status;
275     }
276 
277     NtClose(FileHandle);
278     Size->QuadPart = StandardInfo.AllocationSize.QuadPart;
279     return STATUS_SUCCESS;
280 }
281 
282 NTSTATUS
283 NTAPI
284 SmpDeletePagingFile(IN PUNICODE_STRING FileName)
285 {
286     NTSTATUS Status;
287     OBJECT_ATTRIBUTES ObjectAttributes;
288     IO_STATUS_BLOCK IoStatusBlock;
289     HANDLE FileHandle;
290     FILE_DISPOSITION_INFORMATION Disposition;
291 
292     /* Open the page file */
293     InitializeObjectAttributes(&ObjectAttributes,
294                                FileName,
295                                OBJ_CASE_INSENSITIVE,
296                                NULL,
297                                NULL);
298     Status = NtOpenFile(&FileHandle,
299                         DELETE,
300                         &ObjectAttributes,
301                         &IoStatusBlock,
302                         FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
303                         FILE_NON_DIRECTORY_FILE);
304     if (NT_SUCCESS(Status))
305     {
306         /* Delete it */
307         Disposition.DeleteFile = TRUE;
308         Status = NtSetInformationFile(FileHandle,
309                                       &IoStatusBlock,
310                                       &Disposition,
311                                       sizeof(Disposition),
312                                       FileDispositionInformation);
313         if (!NT_SUCCESS(Status))
314         {
315             DPRINT1("SMSS:PFILE: Failed to delete page file `%wZ' (status %X)\n",
316                     FileName, Status);
317         }
318         else
319         {
320             DPRINT("SMSS:PFILE: Deleted stale paging file - %wZ\n", FileName);
321         }
322 
323         /* Close the handle */
324         NtClose(FileHandle);
325     }
326     else
327     {
328         DPRINT1("SMSS:PFILE: Failed to open for deletion page file `%wZ' (status %X)\n",
329                 FileName, Status);
330     }
331 
332     /* All done */
333     return Status;
334 }
335 
336 NTSTATUS
337 NTAPI
338 SmpGetVolumeFreeSpace(IN PSMP_VOLUME_DESCRIPTOR Volume)
339 {
340     NTSTATUS Status;
341     LARGE_INTEGER FreeSpace, FinalFreeSpace;
342     FILE_FS_SIZE_INFORMATION SizeInfo;
343     IO_STATUS_BLOCK IoStatusBlock;
344     OBJECT_ATTRIBUTES ObjectAttributes;
345     UNICODE_STRING VolumeName;
346     HANDLE VolumeHandle;
347     WCHAR PathString[32];
348     ASSERT(Volume->Flags & SMP_VOLUME_IS_BOOT); // ASSERT says "BootVolume == 1"
349 
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);
355 
356     /* Open the volume */
357     InitializeObjectAttributes(&ObjectAttributes,
358                                &VolumeName,
359                                OBJ_CASE_INSENSITIVE,
360                                NULL,
361                                NULL);
362     Status = NtOpenFile(&VolumeHandle,
363                         FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | SYNCHRONIZE,
364                         &ObjectAttributes,
365                         &IoStatusBlock,
366                         FILE_SHARE_READ | FILE_SHARE_WRITE,
367                         FILE_SYNCHRONOUS_IO_NONALERT | FILE_DIRECTORY_FILE);
368     if (!NT_SUCCESS(Status))
369     {
370         DPRINT1("SMSS:PFILE: Open volume `%wZ' failed with status %X\n", &VolumeName, Status);
371         return Status;
372     }
373 
374     /* Now get size information on the volume */
375     Status = NtQueryVolumeInformationFile(VolumeHandle,
376                                           &IoStatusBlock,
377                                           &SizeInfo,
378                                           sizeof(SizeInfo),
379                                           FileFsSizeInformation);
380     if (!NT_SUCCESS(Status))
381     {
382         /* We failed */
383         DPRINT1("SMSS:PFILE: Query volume `%wZ' (handle %p) for size failed"
384                 " with status %X\n",
385                 &VolumeName,
386                 VolumeHandle,
387                 Status);
388         NtClose(VolumeHandle);
389         return Status;
390     }
391     NtClose(VolumeHandle);
392 
393     /* Compute how much free space we have */
394     FreeSpace.QuadPart = SizeInfo.AvailableAllocationUnits.QuadPart *
395                          SizeInfo.SectorsPerAllocationUnit;
396     FinalFreeSpace.QuadPart = FreeSpace.QuadPart * SizeInfo.BytesPerSector;
397 
398     /* Check if there's less than 32MB free so we don't starve the disk */
399     if (FinalFreeSpace.QuadPart <= MINIMUM_TO_KEEP_FREE)
400     {
401         /* In this case, act as if there's no free space  */
402         Volume->FreeSpace.QuadPart = 0;
403     }
404     else
405     {
406         /* Trim off 32MB to give the disk a bit of breathing room */
407         Volume->FreeSpace.QuadPart = FinalFreeSpace.QuadPart -
408                                      MINIMUM_TO_KEEP_FREE;
409     }
410 
411     return STATUS_SUCCESS;
412 }
413 
414 PSMP_VOLUME_DESCRIPTOR
415 NTAPI
416 SmpSearchVolumeDescriptor(IN WCHAR DriveLetter)
417 {
418     WCHAR UpLetter;
419     PSMP_VOLUME_DESCRIPTOR Volume = NULL;
420     PLIST_ENTRY NextEntry;
421 
422     /* Use upper case to reduce differences */
423     UpLetter = RtlUpcaseUnicodeChar(DriveLetter);
424 
425     /* Loop each volume */
426     NextEntry = SmpVolumeDescriptorList.Flink;
427     while (NextEntry != &SmpVolumeDescriptorList)
428     {
429         /* Grab the entry */
430         Volume = CONTAINING_RECORD(NextEntry, SMP_VOLUME_DESCRIPTOR, Entry);
431 
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');
435 
436         /* Break if it matches, if not, keep going */
437         if (Volume->DriveLetter == UpLetter) break;
438         NextEntry = NextEntry->Flink;
439     }
440 
441     /* Return the volume if one was found */
442     if (NextEntry == &SmpVolumeDescriptorList) Volume = NULL;
443     return Volume;
444 }
445 
446 NTSTATUS
447 NTAPI
448 SmpCreatePagingFile(IN PUNICODE_STRING Name,
449                     IN PLARGE_INTEGER MinSize,
450                     IN PLARGE_INTEGER MaxSize,
451                     IN ULONG Priority)
452 {
453     NTSTATUS Status;
454 
455     /* Tell the kernel to create the pagefile */
456     Status = NtCreatePagingFile(Name, MinSize, MaxSize, Priority);
457     if (NT_SUCCESS(Status))
458     {
459         DPRINT("SMSS:PFILE: NtCreatePagingFile (%wZ, %I64X, %I64X) succeeded.\n",
460                 Name,
461                 MinSize->QuadPart,
462                 MaxSize->QuadPart);
463     }
464     else
465     {
466         DPRINT1("SMSS:PFILE: NtCreatePagingFile (%wZ, %I64X, %I64X) failed with %X\n",
467                 Name,
468                 MinSize->QuadPart,
469                 MaxSize->QuadPart,
470                 Status);
471     }
472 
473     /* Return the status */
474     return Status;
475 }
476 
477 NTSTATUS
478 NTAPI
479 SmpCreatePagingFileOnFixedDrive(IN PSMP_PAGEFILE_DESCRIPTOR Descriptor,
480                                 IN PLARGE_INTEGER FuzzFactor,
481                                 IN PLARGE_INTEGER MinimumSize)
482 {
483     PSMP_VOLUME_DESCRIPTOR Volume;
484     BOOLEAN ShouldDelete;
485     NTSTATUS Status;
486     LARGE_INTEGER PageFileSize;
487     ASSERT(Descriptor->Name.Buffer[STANDARD_DRIVE_LETTER_OFFSET] != L'?');
488 
489     /* Try to find the volume descriptor for this drive letter */
490     ShouldDelete = FALSE;
491     Volume = SmpSearchVolumeDescriptor(Descriptor->Name.Buffer[STANDARD_DRIVE_LETTER_OFFSET]);
492     if (!Volume)
493     {
494         /* Couldn't find it, fail */
495         DPRINT1("SMSS:PFILE: No volume descriptor for `%wZ'\n",
496                 &Descriptor->Name);
497         return STATUS_INVALID_PARAMETER;
498     }
499 
500     /* Check if this is the boot volume */
501     if (Volume->Flags & SMP_VOLUME_IS_BOOT)
502     {
503         /* Check if we haven't yet processed a crash dump on this volume */
504         if (!(Descriptor->Flags & SMP_PAGEFILE_DUMP_PROCESSED))
505         {
506             /* Try to find a crash dump and extract it */
507             DPRINT("SMSS:PFILE: Checking for crash dump in `%wZ' on boot volume\n",
508                     &Descriptor->Name);
509             SmpCheckForCrashDump(&Descriptor->Name);
510 
511             /* Update how much free space we have now that we extracted a dump */
512             Status = SmpGetVolumeFreeSpace(Volume);
513             if (!NT_SUCCESS(Status))
514             {
515                 DPRINT1("SMSS:PFILE: Failed to query free space for boot volume `%wC'\n",
516                         Volume->DriveLetter);
517             }
518             else
519             {
520                 DPRINT("Queried free space for boot volume `%wC: %I64x'\n",
521                         Volume->DriveLetter, Volume->FreeSpace.QuadPart);
522             }
523 
524             /* Don't process crashdump on this volume anymore */
525             Descriptor->Flags |= SMP_PAGEFILE_DUMP_PROCESSED;
526         }
527     }
528     else
529     {
530         /* Crashdumps can only be on the boot volume */
531         DPRINT("SMSS:PFILE: Skipping crash dump checking for `%wZ' on non boot"
532                 "volume `%wC'\n",
533                 &Descriptor->Name,
534                 Volume->DriveLetter);
535     }
536 
537     /* Update the size after dump extraction */
538     Descriptor->ActualMinSize = Descriptor->MinSize;
539     Descriptor->ActualMaxSize = Descriptor->MaxSize;
540 
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",
545             PageFileSize,
546             &Descriptor->Name);
547     DPRINT("SMSS:PFILE: Free space on volume `%wC' is %I64X\n",
548             Volume->DriveLetter,
549             Volume->FreeSpace.QuadPart);
550 
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)
554     {
555         Descriptor->ActualMinSize = PageFileSize;
556     }
557     if (Descriptor->ActualMaxSize.QuadPart > PageFileSize.QuadPart)
558     {
559         Descriptor->ActualMaxSize = PageFileSize;
560     }
561     DPRINT("SMSS:PFILE: min %I64X, max %I64X, real min %I64X\n",
562             Descriptor->ActualMinSize.QuadPart,
563             Descriptor->ActualMaxSize.QuadPart,
564             MinimumSize->QuadPart);
565 
566     /* Keep going until we've created a pagefile of the right size */
567     while (Descriptor->ActualMinSize.QuadPart >= MinimumSize->QuadPart)
568     {
569         /* Call NT to do it */
570         Status = SmpCreatePagingFile(&Descriptor->Name,
571                                      &Descriptor->ActualMinSize,
572                                      &Descriptor->ActualMaxSize,
573                                      0);
574         if (NT_SUCCESS(Status))
575         {
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++;
580             break;
581         }
582 
583         /* We failed, try a slightly smaller pagefile */
584         Descriptor->ActualMinSize.QuadPart -= FuzzFactor->QuadPart;
585     }
586 
587     /* Check if we weren't able to create it */
588     if (Descriptor->ActualMinSize.QuadPart < MinimumSize->QuadPart)
589     {
590         /* Delete the current page file and fail */
591         if (ShouldDelete)
592         {
593             SmpDeletePagingFile(&Descriptor->Name);
594 
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;
597         }
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;
603     }
604 
605     /* Return the status */
606     return Status;
607 }
608 
609 NTSTATUS
610 NTAPI
611 SmpCreatePagingFileOnAnyDrive(IN PSMP_PAGEFILE_DESCRIPTOR Descriptor,
612                               IN PLARGE_INTEGER FuzzFactor,
613                               IN PLARGE_INTEGER MinimumSize)
614 {
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'?');
619 
620     /* Loop the volume list */
621     NextEntry = SmpVolumeDescriptorList.Flink;
622     while (NextEntry != &SmpVolumeDescriptorList)
623     {
624         /* Get the volume */
625         Volume = CONTAINING_RECORD(NextEntry, SMP_VOLUME_DESCRIPTOR, Entry);
626 
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');
630 
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,
634                                                  FuzzFactor,
635                                                  MinimumSize);
636         if (NT_SUCCESS(Status)) break;
637 
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;
641     }
642 
643     /* Return disk full or success */
644     return Status;
645 }
646 
647 VOID
648 NTAPI
649 SmpMakeDefaultPagingFileDescriptor(IN PSMP_PAGEFILE_DESCRIPTOR Descriptor)
650 {
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;
655 }
656 
657 VOID
658 NTAPI
659 SmpMakeSystemManagedPagingFileDescriptor(IN PSMP_PAGEFILE_DESCRIPTOR Descriptor)
660 {
661     NTSTATUS Status;
662     LONGLONG MinimumSize, MaximumSize, Ram;
663     SYSTEM_BASIC_INFORMATION BasicInfo;
664 
665     /* Query the page size of the system, and the amount of RAM */
666     Status = NtQuerySystemInformation(SystemBasicInformation,
667                                       &BasicInfo,
668                                       sizeof(BasicInfo),
669                                       NULL);
670     if (!NT_SUCCESS(Status))
671     {
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);
675         return;
676     }
677 
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;
681 
682     /* If we have more than 1GB, use that as minimum, otherwise, use 1.5X RAM */
683     MinimumSize = (Ram >= 1024 * MEGABYTE) ? Ram : MaximumSize / 2;
684 
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;
689 }
690 
691 NTSTATUS
692 NTAPI
693 SmpValidatePagingFileSizes(IN PSMP_PAGEFILE_DESCRIPTOR Descriptor)
694 {
695     NTSTATUS Status = STATUS_SUCCESS;
696     ULONGLONG MinSize, MaxSize;
697     BOOLEAN WasTooBig = FALSE;
698 
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);
704 
705     /* Don't let minimum be bigger than maximum */
706     if (MinSize > MaxSize) MaxSize = MinSize;
707 
708     /* On PAE we can have bigger pagefiles... */
709     if (SharedUserData->ProcessorFeatures[PF_PAE_ENABLED])
710     {
711         /* But we don't support that yet */
712         DPRINT1("ReactOS does not support PAE yet... assuming sizes OK\n");
713     }
714     else
715     {
716         /* Check if the minimum is more then 4095 MB */
717         if (MinSize > MAXIMUM_PAGEFILE_SIZE)
718         {
719             /* Trim it, this isn't allowed */
720             WasTooBig = TRUE;
721             MinSize = MAXIMUM_PAGEFILE_SIZE;
722         }
723 
724         /* Check if the maximum is more then 4095 MB */
725         if (MaxSize > MAXIMUM_PAGEFILE_SIZE)
726         {
727             /* Trim it, this isn't allowed */
728             WasTooBig = TRUE;
729             MaxSize = MAXIMUM_PAGEFILE_SIZE;
730         }
731     }
732 
733     /* Did we trim? */
734     if (WasTooBig)
735     {
736         /* Notify debugger output and write a flag in the descriptor */
737         DPRINT("SMSS:PFILE: Trimmed size of `%wZ' to maximum allowed\n",
738                 &Descriptor->Name);
739         Descriptor->Flags |= SMP_PAGEFILE_WAS_TOO_BIG;
740     }
741 
742     /* Now write the (possibly trimmed) sizes back */
743     Descriptor->MinSize.QuadPart = MinSize;
744     Descriptor->MaxSize.QuadPart = MaxSize;
745     return Status;
746 }
747 
748 NTSTATUS
749 NTAPI
750 SmpCreateSystemManagedPagingFile(IN PSMP_PAGEFILE_DESCRIPTOR Descriptor,
751                                  IN BOOLEAN DecreaseSize)
752 {
753     LARGE_INTEGER FuzzFactor, Size;
754 
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.
759 
760     /* Keep decreasing the pagefile by this amount if we run out of space */
761     FuzzFactor.QuadPart = FUZZ_FACTOR;
762 
763     /* Create the descriptor for it (mainly the right sizes) and validate */
764     SmpMakeSystemManagedPagingFileDescriptor(Descriptor);
765     SmpValidatePagingFileSizes(Descriptor);
766 
767     /* Use either the minimum size in the descriptor, or 16MB in minimal mode */
768     Size.QuadPart = DecreaseSize ? 16 * MEGABYTE : Descriptor->MinSize.QuadPart;
769 
770     /* Check if this should be a fixed pagefile or an any pagefile*/
771     if (Descriptor->Name.Buffer[STANDARD_DRIVE_LETTER_OFFSET] == '?')
772     {
773         /* Find a disk for it */
774         return SmpCreatePagingFileOnAnyDrive(Descriptor, &FuzzFactor, &Size);
775     }
776 
777     /* Use the disk that was given */
778     return SmpCreatePagingFileOnFixedDrive(Descriptor, &FuzzFactor, &Size);
779 }
780 
781 NTSTATUS
782 NTAPI
783 SmpCreateEmergencyPagingFile(VOID)
784 {
785     PSMP_PAGEFILE_DESCRIPTOR Descriptor;
786     WCHAR Buffer[32];
787 
788     /* Allocate a descriptor */
789     Descriptor = RtlAllocateHeap(RtlGetProcessHeap(),
790                                  HEAP_ZERO_MEMORY,
791                                  sizeof(SMP_PAGEFILE_DESCRIPTOR));
792     if (!Descriptor) return STATUS_NO_MEMORY;
793 
794     /* Initialize it */
795     RtlInitUnicodeString(&Descriptor->Token, NULL);
796 
797     /* Copy the default pagefile name */
798     ASSERT(sizeof(Buffer) >= sizeof(STANDARD_PAGING_FILE_NAME));
799     wcscpy(Buffer, STANDARD_PAGING_FILE_NAME);
800 
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;
807 
808     /* Insert it into the descriptor list */
809     InsertHeadList(&SmpPagingFileDescriptorList, &Descriptor->Entry);
810     SmpNumberOfPagingFiles++;
811 
812     /* Go ahead and create it now, with the minimal size possible */
813     return SmpCreateSystemManagedPagingFile(Descriptor, TRUE);
814 }
815 
816 NTSTATUS
817 NTAPI
818 SmpCreateVolumeDescriptors(VOID)
819 {
820     NTSTATUS Status;
821     UNICODE_STRING VolumePath;
822     BOOLEAN BootVolumeFound = FALSE;
823     WCHAR StartChar, Drive, DriveDiff;
824     HANDLE VolumeHandle;
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;
832     WCHAR Buffer[32];
833 
834     /* We should be starting with an empty list */
835     ASSERT(IsListEmpty(&SmpVolumeDescriptorList));
836 
837     /* Query the device map so we can get the drive letters */
838     Status = NtQueryInformationProcess(NtCurrentProcess(),
839                                        ProcessDeviceMap,
840                                        &ProcessInformation.Query,
841                                        sizeof(ProcessInformation.Query),
842                                        NULL);
843     if (!NT_SUCCESS(Status))
844     {
845         DPRINT1("SMSS:PFILE: Query(ProcessDeviceMap) failed with status %X\n",
846                 Status);
847         return Status;
848     }
849 
850     /* Build the volume string, starting with A: (we'll edit this in place) */
851     wcscpy(Buffer, L"\\??\\A:\\");
852     RtlInitUnicodeString(&VolumePath, Buffer);
853 
854     /* Start with the C drive, except on NEC PC-98 */
855     StartChar = IsNEC_98 ? L'A' : L'C';
856     for (Drive = StartChar, DriveDiff = StartChar - L'A'; Drive <= L'Z'; Drive++, DriveDiff++)
857     {
858         /* Skip the disk if it's not in the drive map */
859         if (!((1 << DriveDiff) & ProcessInformation.Query.DriveMap)) continue;
860 
861         /* Write the drive letter and try to open the volume */
862         VolumePath.Buffer[STANDARD_DRIVE_LETTER_OFFSET] = Drive;
863         InitializeObjectAttributes(&ObjectAttributes,
864                                    &VolumePath,
865                                    OBJ_CASE_INSENSITIVE,
866                                    NULL,
867                                    NULL);
868         Status = NtOpenFile(&VolumeHandle,
869                             FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | SYNCHRONIZE,
870                             &ObjectAttributes,
871                             &IoStatusBlock,
872                             FILE_SHARE_READ | FILE_SHARE_WRITE,
873                             FILE_SYNCHRONOUS_IO_NONALERT | FILE_DIRECTORY_FILE);
874         if (!NT_SUCCESS(Status))
875         {
876             /* Skip the volume if we failed */
877             DPRINT1("SMSS:PFILE: Open volume `%wZ' failed with status %X\n",
878                     &VolumePath, Status);
879             continue;
880         }
881 
882         /* Now query device information on the volume */
883         Status = NtQueryVolumeInformationFile(VolumeHandle,
884                                               &IoStatusBlock,
885                                               &DeviceInfo,
886                                               sizeof(DeviceInfo),
887                                               FileFsDeviceInformation);
888         if (!NT_SUCCESS(Status))
889         {
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",
893                     &VolumePath,
894                     VolumeHandle,
895                     Status);
896             NtClose(VolumeHandle);
897             continue;
898         }
899 
900         /* Check if this is a fixed disk */
901         if (DeviceInfo.Characteristics & (FILE_FLOPPY_DISKETTE |
902                                           FILE_READ_ONLY_DEVICE |
903                                           FILE_REMOTE_DEVICE |
904                                           FILE_REMOVABLE_MEDIA))
905         {
906             /* It isn't, so skip it */
907             DPRINT1("SMSS:PFILE: Volume `%wZ' (%X) cannot store a paging file\n",
908                     &VolumePath,
909                     DeviceInfo.Characteristics);
910             NtClose(VolumeHandle);
911             continue;
912         }
913 
914         /* We found a fixed volume, allocate a descriptor for it */
915         Volume = RtlAllocateHeap(RtlGetProcessHeap(),
916                                  HEAP_ZERO_MEMORY,
917                                  sizeof(SMP_VOLUME_DESCRIPTOR));
918         if (!Volume)
919         {
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);
924             continue;
925         }
926 
927         /* Save the drive letter and device information */
928         Volume->DriveLetter = Drive;
929         Volume->DeviceInfo = DeviceInfo;
930 
931         /* Check if this is the boot volume */
932         if (RtlUpcaseUnicodeChar(Drive) ==
933             RtlUpcaseUnicodeChar(SharedUserData->NtSystemRoot[0]))
934         {
935             /* Save it */
936             ASSERT(BootVolumeFound == FALSE);
937             Volume->Flags |= SMP_VOLUME_IS_BOOT;
938             BootVolumeFound = TRUE;
939         }
940 
941         /* Now get size information on the volume */
942         Status = NtQueryVolumeInformationFile(VolumeHandle,
943                                               &IoStatusBlock,
944                                               &SizeInfo,
945                                               sizeof(SizeInfo),
946                                               FileFsSizeInformation);
947         if (!NT_SUCCESS(Status))
948         {
949             /* We failed -- keep going */
950             DPRINT1("SMSS:PFILE: Query volume `%wZ' (handle %p) for size failed"
951                     " with status %X\n",
952                     &VolumePath,
953                     VolumeHandle,
954                     Status);
955             RtlFreeHeap(RtlGetProcessHeap(), 0, Volume);
956             NtClose(VolumeHandle);
957             continue;
958         }
959 
960         /* Done querying volume information, close the handle */
961         NtClose(VolumeHandle);
962 
963         /* Compute how much free space we have */
964         FreeSpace.QuadPart = SizeInfo.AvailableAllocationUnits.QuadPart *
965                              SizeInfo.SectorsPerAllocationUnit;
966         FinalFreeSpace.QuadPart = FreeSpace.QuadPart * SizeInfo.BytesPerSector;
967 
968         /* Check if there's less than 32MB free so we don't starve the disk */
969         if (FinalFreeSpace.QuadPart <= MINIMUM_TO_KEEP_FREE)
970         {
971             /* In this case, act as if there's no free space  */
972             Volume->FreeSpace.QuadPart = 0;
973         }
974         else
975         {
976             /* Trim off 32MB to give the disk a bit of breathing room */
977             Volume->FreeSpace.QuadPart = FinalFreeSpace.QuadPart -
978                                          MINIMUM_TO_KEEP_FREE;
979         }
980 
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);
985     }
986 
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;
991 
992     /* Something is really messed up if we found no disks at all */
993     return STATUS_UNEXPECTED_IO_ERROR;
994 }
995 
996 NTSTATUS
997 NTAPI
998 SmpCreatePagingFiles(VOID)
999 {
1000     NTSTATUS Status;
1001     PSMP_PAGEFILE_DESCRIPTOR Descriptor;
1002     LARGE_INTEGER Size, FuzzFactor;
1003     BOOLEAN Created = FALSE;
1004     PLIST_ENTRY NextEntry;
1005 
1006     /* Check if no paging files were requested */
1007     if (!(SmpNumberOfPagingFiles) && !(SmpRegistrySpecifierPresent))
1008     {
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;
1013     }
1014 
1015     /* Initialize the volume descriptors so we can know what's available */
1016     Status = SmpCreateVolumeDescriptors();
1017     if (!NT_SUCCESS(Status))
1018     {
1019         /* We can't make decisions without this, so fail */
1020         DPRINT1("SMSS:PFILE: Failed to create volume descriptors (status %X)\n",
1021                 Status);
1022         return Status;
1023     }
1024 
1025     /* If we fail creating pagefiles, try to reduce by this much each time */
1026     FuzzFactor.QuadPart = FUZZ_FACTOR;
1027 
1028     /* Loop the descriptor list */
1029     NextEntry = SmpPagingFileDescriptorList.Flink;
1030     while (NextEntry != &SmpPagingFileDescriptorList)
1031     {
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)
1035         {
1036             /* This is a system-managed descriptor. Create the correct file */
1037             DPRINT("SMSS:PFILE: Creating a system managed paging file (`%wZ')\n",
1038                     &Descriptor->Name);
1039             Status = SmpCreateSystemManagedPagingFile(Descriptor, FALSE);
1040             if (!NT_SUCCESS(Status))
1041             {
1042                 /* We failed -- try again, with size minimization this time */
1043                 DPRINT("SMSS:PFILE: Trying lower sizes for (`%wZ')\n",
1044                         &Descriptor->Name);
1045                 Status = SmpCreateSystemManagedPagingFile(Descriptor, TRUE);
1046             }
1047         }
1048         else
1049         {
1050             /* This is a manually entered descriptor. Validate its size first */
1051             SmpValidatePagingFileSizes(Descriptor);
1052 
1053             /* Check if this is an ANY pagefile or a FIXED pagefile */
1054             DPRINT("SMSS:PFILE: Creating a normal paging file (`%wZ')\n",
1055                     &Descriptor->Name);
1056             if (Descriptor->Name.Buffer[STANDARD_DRIVE_LETTER_OFFSET] == L'?')
1057             {
1058                 /* It's an any pagefile, try to create it wherever possible */
1059                 Size = Descriptor->MinSize;
1060                 Status = SmpCreatePagingFileOnAnyDrive(Descriptor,
1061                                                        &FuzzFactor,
1062                                                        &Size);
1063                 if (!NT_SUCCESS(Status))
1064                 {
1065                     /* We failed to create it. Try again with a smaller size */
1066                     DPRINT("SMSS:PFILE: Trying lower sizes for (`%wZ')\n",
1067                             &Descriptor->Name);
1068                     Size.QuadPart = 16 * MEGABYTE;
1069                     Status = SmpCreatePagingFileOnAnyDrive(Descriptor,
1070                                                            &FuzzFactor,
1071                                                            &Size);
1072                 }
1073             }
1074             else
1075             {
1076                 /* It's a fixed pagefile: override the minimum and use ours */
1077                 Size.QuadPart = 16 * MEGABYTE;
1078                 Status = SmpCreatePagingFileOnFixedDrive(Descriptor,
1079                                                          &FuzzFactor,
1080                                                          &Size);
1081             }
1082         }
1083 
1084         /* Go to the next descriptor */
1085         if (NT_SUCCESS(Status)) Created = TRUE;
1086         NextEntry = NextEntry->Flink;
1087     }
1088 
1089     /* Check if none of the code in our loops above was able to create it */
1090     if (!Created)
1091     {
1092         /* Build an emergency pagefile ourselves */
1093         DPRINT1("SMSS:PFILE: Creating emergency paging file.\n");
1094         Status = SmpCreateEmergencyPagingFile();
1095     }
1096 
1097     /* All done */
1098     return Status;
1099 }
1100