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