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