1 /* 2 * PROJECT: ReactOS Kernel 3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) 4 * PURPOSE: Paging file functions 5 * COPYRIGHT: Copyright 1998-2003 David Welch <welch@mcmail.com> 6 * Copyright 2010-2018 Pierre Schweitzer <pierre@reactos.org> 7 */ 8 9 /* INCLUDES *****************************************************************/ 10 11 #include <ntoskrnl.h> 12 #define NDEBUG 13 #include <debug.h> 14 15 /* GLOBALS *******************************************************************/ 16 17 /* Minimum pagefile size is 256 pages (1 MB) */ 18 #define MINIMUM_PAGEFILE_SIZE (256ULL * PAGE_SIZE) 19 20 /* Maximum pagefile sizes for different architectures */ 21 #if defined(_M_IX86) && !defined(_X86PAE_) 22 /* Around 4 GB */ 23 #define MAXIMUM_PAGEFILE_SIZE ((1ULL * 1024 * 1024 - 1) * PAGE_SIZE) 24 // PAGE_ROUND_DOWN(4ULL * GIGABYTE - 1) 25 /* PAE uses the same size as x64 */ 26 #elif (defined(_M_IX86) && defined(_X86PAE_)) || defined (_M_AMD64) || defined(_M_ARM64) 27 /* Around 16 TB */ 28 #if (NTDDI_VERSION >= NTDDI_WIN10) 29 #define MAXIMUM_PAGEFILE_SIZE ((4ULL * 1024 * 1024 * 1024 - 2) * PAGE_SIZE) 30 // PAGE_ROUND_DOWN(16ULL * TERABYTE - PAGE_SIZE - 1) 31 #else 32 #define MAXIMUM_PAGEFILE_SIZE ((4ULL * 1024 * 1024 * 1024 - 1) * PAGE_SIZE) 33 // PAGE_ROUND_DOWN(16ULL * TERABYTE - 1) 34 #endif 35 #elif defined (_M_IA64) 36 /* Around 32 TB */ 37 #define MAXIMUM_PAGEFILE_SIZE ((8ULL * 1024 * 1024 * 1024 - 1) * PAGE_SIZE) 38 // PAGE_ROUND_DOWN(32ULL * TERABYTE - 1) 39 #elif defined(_M_ARM) 40 /* Around 2 GB */ 41 #if (NTDDI_VERSION >= NTDDI_WIN10) 42 #define MAXIMUM_PAGEFILE_SIZE ((512ULL * 1024 - 2) * PAGE_SIZE) 43 // PAGE_ROUND_DOWN(2ULL * GIGABYTE - PAGE_SIZE - 1) 44 #elif (NTDDI_VERSION >= NTDDI_WINBLUE) // NTDDI_WIN81 45 #define MAXIMUM_PAGEFILE_SIZE ((512ULL * 1024 - 1) * PAGE_SIZE) 46 // PAGE_ROUND_DOWN(2ULL * GIGABYTE - 1) 47 #else 48 /* Around 4 GB */ 49 #define MAXIMUM_PAGEFILE_SIZE ((1ULL * 1024 * 1024 - 1) * PAGE_SIZE) 50 // PAGE_ROUND_DOWN(4ULL * GIGABYTE - 1) 51 #endif 52 #else 53 #error Unknown architecture 54 #endif 55 56 /* List of paging files, both used and free */ 57 PMMPAGING_FILE MmPagingFile[MAX_PAGING_FILES]; 58 59 /* Lock for examining the list of paging files */ 60 KGUARDED_MUTEX MmPageFileCreationLock; 61 62 /* Number of paging files */ 63 ULONG MmNumberOfPagingFiles; 64 65 /* Number of pages that are available for swapping */ 66 PFN_COUNT MiFreeSwapPages; 67 68 /* Number of pages that have been allocated for swapping */ 69 PFN_COUNT MiUsedSwapPages; 70 71 BOOLEAN MmZeroPageFile; 72 73 /* 74 * Number of pages that have been reserved for swapping but not yet allocated 75 */ 76 static PFN_COUNT MiReservedSwapPages; 77 78 /* 79 * Ratio between reserved and available swap pages, e.g. setting this to five 80 * forces one swap page to be available for every five swap pages that are 81 * reserved. Setting this to zero turns off commit checking altogether. 82 */ 83 #define MM_PAGEFILE_COMMIT_RATIO (1) 84 85 /* 86 * Number of pages that can be used for potentially swapable memory without 87 * pagefile space being reserved. The intention is that this allows smss 88 * to start up and create page files while ordinarily having a commit 89 * ratio of one. 90 */ 91 #define MM_PAGEFILE_COMMIT_GRACE (256) 92 93 /* 94 * Translate between a swap entry and a file and offset pair. 95 */ 96 #define FILE_FROM_ENTRY(i) ((i) & 0x0f) 97 #define OFFSET_FROM_ENTRY(i) ((i) >> 11) 98 #define ENTRY_FROM_FILE_OFFSET(i, j) ((i) | ((j) << 11) | 0x400) 99 100 /* Make sure there can be only 16 paging files */ 101 C_ASSERT(FILE_FROM_ENTRY(0xffffffff) < MAX_PAGING_FILES); 102 103 static BOOLEAN MmSwapSpaceMessage = FALSE; 104 105 static BOOLEAN MmSystemPageFileLocated = FALSE; 106 107 /* FUNCTIONS *****************************************************************/ 108 109 VOID 110 NTAPI 111 MmBuildMdlFromPages(PMDL Mdl, PPFN_NUMBER Pages) 112 { 113 memcpy(Mdl + 1, Pages, sizeof(PFN_NUMBER) * (PAGE_ROUND_UP(Mdl->ByteOffset+Mdl->ByteCount)/PAGE_SIZE)); 114 } 115 116 117 BOOLEAN 118 NTAPI 119 MmIsFileObjectAPagingFile(PFILE_OBJECT FileObject) 120 { 121 ULONG i; 122 123 /* Loop through all the paging files */ 124 for (i = 0; i < MmNumberOfPagingFiles; i++) 125 { 126 /* Check if this is one of them */ 127 if (MmPagingFile[i]->FileObject == FileObject) return TRUE; 128 } 129 130 /* Nothing found */ 131 return FALSE; 132 } 133 134 VOID 135 NTAPI 136 MmShowOutOfSpaceMessagePagingFile(VOID) 137 { 138 if (!MmSwapSpaceMessage) 139 { 140 DPRINT1("MM: Out of swap space.\n"); 141 MmSwapSpaceMessage = TRUE; 142 } 143 } 144 145 NTSTATUS 146 NTAPI 147 MmWriteToSwapPage(SWAPENTRY SwapEntry, PFN_NUMBER Page) 148 { 149 ULONG i; 150 ULONG_PTR offset; 151 LARGE_INTEGER file_offset; 152 IO_STATUS_BLOCK Iosb; 153 NTSTATUS Status; 154 KEVENT Event; 155 UCHAR MdlBase[sizeof(MDL) + sizeof(PFN_NUMBER)]; 156 PMDL Mdl = (PMDL)MdlBase; 157 158 DPRINT("MmWriteToSwapPage\n"); 159 160 if (SwapEntry == 0) 161 { 162 KeBugCheck(MEMORY_MANAGEMENT); 163 return(STATUS_UNSUCCESSFUL); 164 } 165 166 i = FILE_FROM_ENTRY(SwapEntry); 167 offset = OFFSET_FROM_ENTRY(SwapEntry) - 1; 168 169 if (MmPagingFile[i]->FileObject == NULL || 170 MmPagingFile[i]->FileObject->DeviceObject == NULL) 171 { 172 DPRINT1("Bad paging file 0x%.8X\n", SwapEntry); 173 KeBugCheck(MEMORY_MANAGEMENT); 174 } 175 176 MmInitializeMdl(Mdl, NULL, PAGE_SIZE); 177 MmBuildMdlFromPages(Mdl, &Page); 178 Mdl->MdlFlags |= MDL_PAGES_LOCKED; 179 180 file_offset.QuadPart = offset * PAGE_SIZE; 181 182 KeInitializeEvent(&Event, NotificationEvent, FALSE); 183 Status = IoSynchronousPageWrite(MmPagingFile[i]->FileObject, 184 Mdl, 185 &file_offset, 186 &Event, 187 &Iosb); 188 if (Status == STATUS_PENDING) 189 { 190 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); 191 Status = Iosb.Status; 192 } 193 194 if (Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA) 195 { 196 MmUnmapLockedPages (Mdl->MappedSystemVa, Mdl); 197 } 198 return(Status); 199 } 200 201 202 NTSTATUS 203 NTAPI 204 MmReadFromSwapPage(SWAPENTRY SwapEntry, PFN_NUMBER Page) 205 { 206 return MiReadPageFile(Page, FILE_FROM_ENTRY(SwapEntry), OFFSET_FROM_ENTRY(SwapEntry)); 207 } 208 209 NTSTATUS 210 NTAPI 211 MiReadPageFile( 212 _In_ PFN_NUMBER Page, 213 _In_ ULONG PageFileIndex, 214 _In_ ULONG_PTR PageFileOffset) 215 { 216 LARGE_INTEGER file_offset; 217 IO_STATUS_BLOCK Iosb; 218 NTSTATUS Status; 219 KEVENT Event; 220 UCHAR MdlBase[sizeof(MDL) + sizeof(PFN_NUMBER)]; 221 PMDL Mdl = (PMDL)MdlBase; 222 PMMPAGING_FILE PagingFile; 223 224 DPRINT("MiReadSwapFile\n"); 225 226 if (PageFileOffset == 0) 227 { 228 KeBugCheck(MEMORY_MANAGEMENT); 229 return(STATUS_UNSUCCESSFUL); 230 } 231 232 /* Normalize offset. */ 233 PageFileOffset--; 234 235 ASSERT(PageFileIndex < MAX_PAGING_FILES); 236 237 PagingFile = MmPagingFile[PageFileIndex]; 238 239 if (PagingFile->FileObject == NULL || PagingFile->FileObject->DeviceObject == NULL) 240 { 241 DPRINT1("Bad paging file %u\n", PageFileIndex); 242 KeBugCheck(MEMORY_MANAGEMENT); 243 } 244 245 MmInitializeMdl(Mdl, NULL, PAGE_SIZE); 246 MmBuildMdlFromPages(Mdl, &Page); 247 Mdl->MdlFlags |= MDL_PAGES_LOCKED | MDL_IO_PAGE_READ; 248 249 file_offset.QuadPart = PageFileOffset * PAGE_SIZE; 250 251 KeInitializeEvent(&Event, NotificationEvent, FALSE); 252 Status = IoPageRead(PagingFile->FileObject, 253 Mdl, 254 &file_offset, 255 &Event, 256 &Iosb); 257 if (Status == STATUS_PENDING) 258 { 259 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); 260 Status = Iosb.Status; 261 } 262 if (Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA) 263 { 264 MmUnmapLockedPages (Mdl->MappedSystemVa, Mdl); 265 } 266 return(Status); 267 } 268 269 CODE_SEG("INIT") 270 VOID 271 NTAPI 272 MmInitPagingFile(VOID) 273 { 274 ULONG i; 275 276 KeInitializeGuardedMutex(&MmPageFileCreationLock); 277 278 MiFreeSwapPages = 0; 279 MiUsedSwapPages = 0; 280 MiReservedSwapPages = 0; 281 282 for (i = 0; i < MAX_PAGING_FILES; i++) 283 { 284 MmPagingFile[i] = NULL; 285 } 286 MmNumberOfPagingFiles = 0; 287 } 288 289 VOID 290 NTAPI 291 MmFreeSwapPage(SWAPENTRY Entry) 292 { 293 ULONG i; 294 ULONG_PTR off; 295 PMMPAGING_FILE PagingFile; 296 297 i = FILE_FROM_ENTRY(Entry); 298 off = OFFSET_FROM_ENTRY(Entry) - 1; 299 300 KeAcquireGuardedMutex(&MmPageFileCreationLock); 301 302 PagingFile = MmPagingFile[i]; 303 if (PagingFile == NULL) 304 { 305 KeBugCheck(MEMORY_MANAGEMENT); 306 } 307 308 RtlClearBit(PagingFile->Bitmap, off >> 5); 309 310 PagingFile->FreeSpace++; 311 PagingFile->CurrentUsage--; 312 313 MiFreeSwapPages++; 314 MiUsedSwapPages--; 315 UpdateTotalCommittedPages(-1); 316 317 KeReleaseGuardedMutex(&MmPageFileCreationLock); 318 } 319 320 SWAPENTRY 321 NTAPI 322 MmAllocSwapPage(VOID) 323 { 324 ULONG i; 325 ULONG off; 326 SWAPENTRY entry; 327 328 KeAcquireGuardedMutex(&MmPageFileCreationLock); 329 330 if (MiFreeSwapPages == 0) 331 { 332 KeReleaseGuardedMutex(&MmPageFileCreationLock); 333 return(0); 334 } 335 336 for (i = 0; i < MAX_PAGING_FILES; i++) 337 { 338 if (MmPagingFile[i] != NULL && 339 MmPagingFile[i]->FreeSpace >= 1) 340 { 341 off = RtlFindClearBitsAndSet(MmPagingFile[i]->Bitmap, 1, 0); 342 if (off == 0xFFFFFFFF) 343 { 344 KeBugCheck(MEMORY_MANAGEMENT); 345 KeReleaseGuardedMutex(&MmPageFileCreationLock); 346 return(STATUS_UNSUCCESSFUL); 347 } 348 MiUsedSwapPages++; 349 MiFreeSwapPages--; 350 UpdateTotalCommittedPages(1); 351 352 KeReleaseGuardedMutex(&MmPageFileCreationLock); 353 354 entry = ENTRY_FROM_FILE_OFFSET(i, off + 1); 355 return(entry); 356 } 357 } 358 359 KeReleaseGuardedMutex(&MmPageFileCreationLock); 360 KeBugCheck(MEMORY_MANAGEMENT); 361 return(0); 362 } 363 364 NTSTATUS 365 NTAPI 366 NtCreatePagingFile( 367 _In_ PUNICODE_STRING FileName, 368 _In_ PLARGE_INTEGER MinimumSize, 369 _In_ PLARGE_INTEGER MaximumSize, 370 _In_ ULONG Reserved) 371 { 372 NTSTATUS Status; 373 OBJECT_ATTRIBUTES ObjectAttributes; 374 HANDLE FileHandle; 375 IO_STATUS_BLOCK IoStatus; 376 PFILE_OBJECT FileObject; 377 PMMPAGING_FILE PagingFile; 378 SIZE_T AllocMapSize; 379 ULONG Count; 380 KPROCESSOR_MODE PreviousMode; 381 UNICODE_STRING PageFileName; 382 LARGE_INTEGER SafeMinimumSize, SafeMaximumSize, AllocationSize; 383 FILE_FS_DEVICE_INFORMATION FsDeviceInfo; 384 SECURITY_DESCRIPTOR SecurityDescriptor; 385 PACL Dacl; 386 PWSTR Buffer; 387 DEVICE_TYPE DeviceType; 388 389 PAGED_CODE(); 390 391 DPRINT("NtCreatePagingFile(FileName: '%wZ', MinimumSize: %I64d, MaximumSize: %I64d)\n", 392 FileName, MinimumSize->QuadPart, MaximumSize->QuadPart); 393 394 if (MmNumberOfPagingFiles >= MAX_PAGING_FILES) 395 { 396 return STATUS_TOO_MANY_PAGING_FILES; 397 } 398 399 PreviousMode = ExGetPreviousMode(); 400 401 if (PreviousMode != KernelMode) 402 { 403 if (SeSinglePrivilegeCheck(SeCreatePagefilePrivilege, PreviousMode) != TRUE) 404 { 405 return STATUS_PRIVILEGE_NOT_HELD; 406 } 407 408 _SEH2_TRY 409 { 410 SafeMinimumSize = ProbeForReadLargeInteger(MinimumSize); 411 SafeMaximumSize = ProbeForReadLargeInteger(MaximumSize); 412 PageFileName = ProbeForReadUnicodeString(FileName); 413 } 414 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 415 { 416 /* Return the exception code */ 417 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 418 } 419 _SEH2_END; 420 } 421 else 422 { 423 SafeMinimumSize = *MinimumSize; 424 SafeMaximumSize = *MaximumSize; 425 PageFileName = *FileName; 426 } 427 428 /* 429 * Pagefiles cannot be larger than the platform-specific memory addressable 430 * limits, and of course the minimum should be smaller than the maximum. 431 */ 432 if (SafeMinimumSize.QuadPart < MINIMUM_PAGEFILE_SIZE || 433 SafeMinimumSize.QuadPart > MAXIMUM_PAGEFILE_SIZE) 434 { 435 return STATUS_INVALID_PARAMETER_2; 436 } 437 if (SafeMaximumSize.QuadPart < SafeMinimumSize.QuadPart || 438 SafeMaximumSize.QuadPart > MAXIMUM_PAGEFILE_SIZE) 439 { 440 return STATUS_INVALID_PARAMETER_3; 441 } 442 443 /* Validate the name length */ 444 if ((PageFileName.Length == 0) || 445 (PageFileName.Length > MAXIMUM_FILENAME_LENGTH)) 446 { 447 return STATUS_OBJECT_NAME_INVALID; 448 } 449 450 /* Allocate a buffer to keep the name copy. Note that it is kept only 451 * for information purposes, so it gets allocated in the paged pool, 452 * even if it will be stored in the PagingFile structure, that is 453 * allocated from non-paged pool (see below). */ 454 PageFileName.MaximumLength = PageFileName.Length; 455 Buffer = ExAllocatePoolWithTag(PagedPool, PageFileName.Length, TAG_MM); 456 if (Buffer == NULL) 457 { 458 return STATUS_INSUFFICIENT_RESOURCES; 459 } 460 461 /* Copy the name */ 462 if (PreviousMode != KernelMode) 463 { 464 _SEH2_TRY 465 { 466 ProbeForRead(PageFileName.Buffer, PageFileName.Length, sizeof(WCHAR)); 467 RtlCopyMemory(Buffer, PageFileName.Buffer, PageFileName.Length); 468 } 469 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 470 { 471 ExFreePoolWithTag(Buffer, TAG_MM); 472 473 /* Return the exception code */ 474 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 475 } 476 _SEH2_END; 477 } 478 else 479 { 480 RtlCopyMemory(Buffer, PageFileName.Buffer, PageFileName.Length); 481 } 482 483 /* Replace caller's buffer with ours */ 484 PageFileName.Buffer = Buffer; 485 486 /* Create the security descriptor for the page file */ 487 Status = RtlCreateSecurityDescriptor(&SecurityDescriptor, SECURITY_DESCRIPTOR_REVISION); 488 if (!NT_SUCCESS(Status)) 489 { 490 ExFreePoolWithTag(Buffer, TAG_MM); 491 return Status; 492 } 493 494 /* Create the DACL: we will only allow two SIDs */ 495 Count = sizeof(ACL) + (sizeof(ACE) + RtlLengthSid(SeLocalSystemSid)) + 496 (sizeof(ACE) + RtlLengthSid(SeAliasAdminsSid)); 497 Dacl = ExAllocatePoolWithTag(PagedPool, Count, TAG_DACL); 498 if (Dacl == NULL) 499 { 500 ExFreePoolWithTag(Buffer, TAG_MM); 501 return STATUS_INSUFFICIENT_RESOURCES; 502 } 503 504 /* Initialize the DACL */ 505 Status = RtlCreateAcl(Dacl, Count, ACL_REVISION); 506 if (!NT_SUCCESS(Status)) 507 goto EarlyQuit; 508 509 /* Grant full access to admins */ 510 Status = RtlAddAccessAllowedAce(Dacl, ACL_REVISION, FILE_ALL_ACCESS, SeAliasAdminsSid); 511 if (!NT_SUCCESS(Status)) 512 goto EarlyQuit; 513 514 /* Grant full access to SYSTEM */ 515 Status = RtlAddAccessAllowedAce(Dacl, ACL_REVISION, FILE_ALL_ACCESS, SeLocalSystemSid); 516 if (!NT_SUCCESS(Status)) 517 goto EarlyQuit; 518 519 /* Attach the DACL to the security descriptor */ 520 Status = RtlSetDaclSecurityDescriptor(&SecurityDescriptor, TRUE, Dacl, FALSE); 521 if (!NT_SUCCESS(Status)) 522 goto EarlyQuit; 523 524 InitializeObjectAttributes(&ObjectAttributes, 525 &PageFileName, 526 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, 527 NULL, 528 &SecurityDescriptor); 529 530 /* Make sure we can at least store a complete page: 531 * If we have 2048 BytesPerAllocationUnit (FAT16 < 128MB) there is 532 * a problem if the paging file is fragmented. Suppose the first cluster 533 * of the paging file is cluster 3042 but cluster 3043 is NOT part of the 534 * paging file but of another file. We can't write a complete page (4096 535 * bytes) to the physical location of cluster 3042 then. */ 536 AllocationSize.QuadPart = SafeMinimumSize.QuadPart + PAGE_SIZE; 537 538 /* First, attempt to replace the page file, if existing */ 539 Status = IoCreateFile(&FileHandle, 540 SYNCHRONIZE | WRITE_DAC | FILE_READ_DATA | FILE_WRITE_DATA, 541 &ObjectAttributes, 542 &IoStatus, 543 &AllocationSize, 544 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN, 545 FILE_SHARE_WRITE, 546 FILE_SUPERSEDE, 547 FILE_DELETE_ON_CLOSE | FILE_NO_COMPRESSION | FILE_NO_INTERMEDIATE_BUFFERING, 548 NULL, 549 0, 550 CreateFileTypeNone, 551 NULL, 552 IO_OPEN_PAGING_FILE | IO_NO_PARAMETER_CHECKING); 553 /* If we failed, relax a bit constraints, someone may be already holding the 554 * the file, so share write, don't attempt to replace and don't delete on close 555 * (basically, don't do anything conflicting). 556 * This can happen if the caller attempts to extend a page file. 557 */ 558 if (!NT_SUCCESS(Status)) 559 { 560 ULONG i; 561 562 Status = IoCreateFile(&FileHandle, 563 SYNCHRONIZE | FILE_WRITE_DATA, 564 &ObjectAttributes, 565 &IoStatus, 566 &AllocationSize, 567 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN, 568 FILE_SHARE_WRITE | FILE_SHARE_READ, 569 FILE_OPEN, 570 FILE_NO_COMPRESSION | FILE_NO_INTERMEDIATE_BUFFERING, 571 NULL, 572 0, 573 CreateFileTypeNone, 574 NULL, 575 IO_OPEN_PAGING_FILE | IO_NO_PARAMETER_CHECKING); 576 if (!NT_SUCCESS(Status)) 577 goto EarlyQuit; 578 579 /* We opened it! Check we are that "someone" ;-) 580 * First, get the opened file object. 581 */ 582 Status = ObReferenceObjectByHandle(FileHandle, 583 FILE_READ_DATA | FILE_WRITE_DATA, 584 IoFileObjectType, 585 KernelMode, 586 (PVOID*)&FileObject, 587 NULL); 588 if (!NT_SUCCESS(Status)) 589 { 590 ZwClose(FileHandle); 591 goto EarlyQuit; 592 } 593 594 /* Find if it matches a previous page file */ 595 PagingFile = NULL; 596 597 KeAcquireGuardedMutex(&MmPageFileCreationLock); 598 599 for (i = 0; i < MmNumberOfPagingFiles; ++i) 600 { 601 if (MmPagingFile[i]->FileObject->SectionObjectPointer == FileObject->SectionObjectPointer) 602 { 603 /* Same object pointer: this is the matching page file */ 604 PagingFile = MmPagingFile[i]; 605 break; 606 } 607 } 608 609 /* If we didn't find the page file, fail */ 610 if (PagingFile == NULL) 611 { 612 KeReleaseGuardedMutex(&MmPageFileCreationLock); 613 ObDereferenceObject(FileObject); 614 ZwClose(FileHandle); 615 Status = STATUS_NOT_FOUND; 616 goto EarlyQuit; 617 } 618 619 /* Don't allow page file shrinking */ 620 if (PagingFile->MinimumSize > (SafeMinimumSize.QuadPart >> PAGE_SHIFT)) 621 { 622 KeReleaseGuardedMutex(&MmPageFileCreationLock); 623 ObDereferenceObject(FileObject); 624 ZwClose(FileHandle); 625 Status = STATUS_INVALID_PARAMETER_2; 626 goto EarlyQuit; 627 } 628 629 if ((SafeMaximumSize.QuadPart >> PAGE_SHIFT) < PagingFile->MaximumSize) 630 { 631 KeReleaseGuardedMutex(&MmPageFileCreationLock); 632 ObDereferenceObject(FileObject); 633 ZwClose(FileHandle); 634 Status = STATUS_INVALID_PARAMETER_3; 635 goto EarlyQuit; 636 } 637 638 /* FIXME: implement parameters checking and page file extension */ 639 UNIMPLEMENTED; 640 641 KeReleaseGuardedMutex(&MmPageFileCreationLock); 642 ObDereferenceObject(FileObject); 643 ZwClose(FileHandle); 644 Status = STATUS_NOT_IMPLEMENTED; 645 goto EarlyQuit; 646 } 647 648 if (!NT_SUCCESS(Status)) 649 { 650 EarlyQuit: 651 DPRINT1("Failed creating page file: %lx\n", Status); 652 ExFreePoolWithTag(Dacl, TAG_DACL); 653 ExFreePoolWithTag(Buffer, TAG_MM); 654 return Status; 655 } 656 657 /* Set the security descriptor */ 658 if (NT_SUCCESS(IoStatus.Status)) 659 { 660 Status = ZwSetSecurityObject(FileHandle, DACL_SECURITY_INFORMATION, &SecurityDescriptor); 661 if (!NT_SUCCESS(Status)) 662 { 663 ZwClose(FileHandle); 664 ExFreePoolWithTag(Dacl, TAG_DACL); 665 ExFreePoolWithTag(Buffer, TAG_MM); 666 return Status; 667 } 668 } 669 670 /* DACL is no longer needed, free it */ 671 ExFreePoolWithTag(Dacl, TAG_DACL); 672 673 /* FIXME: To enable once page file management is moved to ARM3 */ 674 #if 0 675 /* Check we won't overflow commit limit with the page file */ 676 if (MmTotalCommitLimitMaximum + (SafeMaximumSize.QuadPart >> PAGE_SHIFT) <= MmTotalCommitLimitMaximum) 677 { 678 ZwClose(FileHandle); 679 ExFreePoolWithTag(Buffer, TAG_MM); 680 return STATUS_INVALID_PARAMETER_3; 681 } 682 #endif 683 684 /* Set its end of file to minimal size */ 685 Status = ZwSetInformationFile(FileHandle, 686 &IoStatus, 687 &SafeMinimumSize, 688 sizeof(LARGE_INTEGER), 689 FileEndOfFileInformation); 690 if (!NT_SUCCESS(Status) || !NT_SUCCESS(IoStatus.Status)) 691 { 692 ZwClose(FileHandle); 693 ExFreePoolWithTag(Buffer, TAG_MM); 694 return Status; 695 } 696 697 Status = ObReferenceObjectByHandle(FileHandle, 698 FILE_READ_DATA | FILE_WRITE_DATA, 699 IoFileObjectType, 700 KernelMode, 701 (PVOID*)&FileObject, 702 NULL); 703 if (!NT_SUCCESS(Status)) 704 { 705 ZwClose(FileHandle); 706 ExFreePoolWithTag(Buffer, TAG_MM); 707 return Status; 708 } 709 710 /* Only allow page file on a few device types */ 711 DeviceType = IoGetRelatedDeviceObject(FileObject)->DeviceType; 712 if (DeviceType != FILE_DEVICE_DISK_FILE_SYSTEM && 713 DeviceType != FILE_DEVICE_NETWORK_FILE_SYSTEM && 714 DeviceType != FILE_DEVICE_DFS_VOLUME && 715 DeviceType != FILE_DEVICE_DFS_FILE_SYSTEM) 716 { 717 ObDereferenceObject(FileObject); 718 ZwClose(FileHandle); 719 ExFreePoolWithTag(Buffer, TAG_MM); 720 return Status; 721 } 722 723 /* Deny page file creation on a floppy disk */ 724 FsDeviceInfo.Characteristics = 0; 725 IoQueryVolumeInformation(FileObject, FileFsDeviceInformation, 726 sizeof(FsDeviceInfo), &FsDeviceInfo, &Count); 727 if (BooleanFlagOn(FsDeviceInfo.Characteristics, FILE_FLOPPY_DISKETTE)) 728 { 729 ObDereferenceObject(FileObject); 730 ZwClose(FileHandle); 731 ExFreePoolWithTag(Buffer, TAG_MM); 732 return STATUS_FLOPPY_VOLUME; 733 } 734 735 /* 736 * Missing validation steps TODO: 737 * (see https://www.geoffchappell.com/studies/windows/km/ntoskrnl/api/mm/modwrite/create.htm ) 738 * 739 * - Verify that no file system driver or any filter driver has done file 740 * I/O while opening the file. 741 * Verify that nothing of the paging file is yet in memory. Specifically, 742 * the file object must either have no SectionObjectPointer or the latter 743 * must have neither a DataSectionObject nor an ImageSectionObject. 744 * Otherwise, we should fail, returning STATUS_INCOMPATIBLE_FILE_MAP. 745 * 746 * - Inform all the applicable drivers to prepare for the possibility of 747 * paging I/O. Much of the point to paging I/O is to resolve page faults. 748 * Especially important is that drivers that handle paging I/O do not 749 * cause more page faults. All the code and data that each driver might 750 * ever use for access to the paging file must be locked into physical 751 * memory. This can’t be left until paging I/O actually occurs. 752 * It must be done in advance. 753 */ 754 755 PagingFile = ExAllocatePoolZero(NonPagedPool, sizeof(*PagingFile), TAG_MM); 756 if (PagingFile == NULL) 757 { 758 ObDereferenceObject(FileObject); 759 ZwClose(FileHandle); 760 ExFreePoolWithTag(Buffer, TAG_MM); 761 return STATUS_INSUFFICIENT_RESOURCES; 762 } 763 764 PagingFile->FileHandle = FileHandle; 765 PagingFile->FileObject = FileObject; 766 PagingFile->Size = (SafeMinimumSize.QuadPart >> PAGE_SHIFT); 767 PagingFile->MinimumSize = PagingFile->Size; 768 PagingFile->MaximumSize = (SafeMaximumSize.QuadPart >> PAGE_SHIFT); 769 /* First page is never used: it's the header 770 * TODO: write it 771 */ 772 PagingFile->FreeSpace = PagingFile->Size - 1; 773 PagingFile->CurrentUsage = 0; 774 PagingFile->PageFileName = PageFileName; 775 ASSERT(PagingFile->Size == PagingFile->FreeSpace + PagingFile->CurrentUsage + 1); 776 777 AllocMapSize = sizeof(RTL_BITMAP) + (((PagingFile->MaximumSize + 31) / 32) * sizeof(ULONG)); 778 PagingFile->Bitmap = ExAllocatePoolWithTag(NonPagedPool, 779 AllocMapSize, 780 TAG_MM); 781 if (PagingFile->Bitmap == NULL) 782 { 783 ExFreePoolWithTag(PagingFile, TAG_MM); 784 ObDereferenceObject(FileObject); 785 ZwClose(FileHandle); 786 ExFreePoolWithTag(Buffer, TAG_MM); 787 return STATUS_INSUFFICIENT_RESOURCES; 788 } 789 790 RtlInitializeBitMap(PagingFile->Bitmap, 791 (PULONG)(PagingFile->Bitmap + 1), 792 (ULONG)(PagingFile->MaximumSize)); 793 RtlClearAllBits(PagingFile->Bitmap); 794 795 /* Insert the new paging file information into the list */ 796 KeAcquireGuardedMutex(&MmPageFileCreationLock); 797 /* Ensure the corresponding slot is empty yet */ 798 ASSERT(MmPagingFile[MmNumberOfPagingFiles] == NULL); 799 MmPagingFile[MmNumberOfPagingFiles] = PagingFile; 800 MmNumberOfPagingFiles++; 801 MiFreeSwapPages = MiFreeSwapPages + PagingFile->FreeSpace; 802 KeReleaseGuardedMutex(&MmPageFileCreationLock); 803 804 MmSwapSpaceMessage = FALSE; 805 806 if (!MmSystemPageFileLocated && BooleanFlagOn(FileObject->DeviceObject->Flags, DO_SYSTEM_BOOT_PARTITION)) 807 { 808 MmSystemPageFileLocated = IoInitializeCrashDump(FileHandle); 809 } 810 811 return STATUS_SUCCESS; 812 } 813 814 /* EOF */ 815