xref: /reactos/ntoskrnl/mm/pagefile.c (revision cce399e7)
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