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