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 } 1151