[MMIXER] Fix additional data size initialization for different audio formats (#6753)
[reactos.git] / base / system / smss / pagefile.c
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
130 SmpPagingFileInitialize(VOID)
131 {
132 /* Initialize the two lists */
133 InitializeListHead(&SmpPagingFileDescriptorList);
134 InitializeListHead(&SmpVolumeDescriptorList);
135 }
136
137 NTSTATUS
138 NTAPI
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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 }