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