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
MmBuildMdlFromPages(PMDL Mdl,PPFN_NUMBER Pages)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
MmIsFileObjectAPagingFile(PFILE_OBJECT FileObject)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
MmShowOutOfSpaceMessagePagingFile(VOID)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
MmWriteToSwapPage(SWAPENTRY SwapEntry,PFN_NUMBER Page)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
MmReadFromSwapPage(SWAPENTRY SwapEntry,PFN_NUMBER Page)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
MiReadPageFile(_In_ PFN_NUMBER Page,_In_ ULONG PageFileIndex,_In_ ULONG_PTR PageFileOffset)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
MmInitPagingFile(VOID)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
MmFreeSwapPage(SWAPENTRY Entry)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
MmAllocSwapPage(VOID)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
NtCreatePagingFile(_In_ PUNICODE_STRING FileName,_In_ PLARGE_INTEGER MinimumSize,_In_ PLARGE_INTEGER MaximumSize,_In_ ULONG Reserved)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