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