xref: /reactos/ntoskrnl/mm/pagefile.c (revision 9393fc32)
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 
299     KeReleaseGuardedMutex(&MmPageFileCreationLock);
300 }
301 
302 SWAPENTRY
303 NTAPI
304 MmAllocSwapPage(VOID)
305 {
306     ULONG i;
307     ULONG off;
308     SWAPENTRY entry;
309 
310     KeAcquireGuardedMutex(&MmPageFileCreationLock);
311 
312     if (MiFreeSwapPages == 0)
313     {
314         KeReleaseGuardedMutex(&MmPageFileCreationLock);
315         return(0);
316     }
317 
318     for (i = 0; i < MAX_PAGING_FILES; i++)
319     {
320         if (MmPagingFile[i] != NULL &&
321                 MmPagingFile[i]->FreeSpace >= 1)
322         {
323             off = RtlFindClearBitsAndSet(MmPagingFile[i]->Bitmap, 1, 0);
324             if (off == 0xFFFFFFFF)
325             {
326                 KeBugCheck(MEMORY_MANAGEMENT);
327                 KeReleaseGuardedMutex(&MmPageFileCreationLock);
328                 return(STATUS_UNSUCCESSFUL);
329             }
330             MiUsedSwapPages++;
331             MiFreeSwapPages--;
332             KeReleaseGuardedMutex(&MmPageFileCreationLock);
333 
334             entry = ENTRY_FROM_FILE_OFFSET(i, off + 1);
335             return(entry);
336         }
337     }
338 
339     KeReleaseGuardedMutex(&MmPageFileCreationLock);
340     KeBugCheck(MEMORY_MANAGEMENT);
341     return(0);
342 }
343 
344 NTSTATUS NTAPI
345 NtCreatePagingFile(IN PUNICODE_STRING FileName,
346                    IN PLARGE_INTEGER MinimumSize,
347                    IN PLARGE_INTEGER MaximumSize,
348                    IN ULONG Reserved)
349 {
350     NTSTATUS Status;
351     OBJECT_ATTRIBUTES ObjectAttributes;
352     HANDLE FileHandle;
353     IO_STATUS_BLOCK IoStatus;
354     PFILE_OBJECT FileObject;
355     PMMPAGING_FILE PagingFile;
356     SIZE_T AllocMapSize;
357     ULONG Count;
358     KPROCESSOR_MODE PreviousMode;
359     UNICODE_STRING PageFileName;
360     LARGE_INTEGER SafeMinimumSize, SafeMaximumSize, AllocationSize;
361     FILE_FS_DEVICE_INFORMATION FsDeviceInfo;
362     SECURITY_DESCRIPTOR SecurityDescriptor;
363     PACL Dacl;
364     PWSTR Buffer;
365     DEVICE_TYPE DeviceType;
366 
367     PAGED_CODE();
368 
369     DPRINT("NtCreatePagingFile(FileName: '%wZ', MinimumSize: %I64d, MaximumSize: %I64d)\n",
370            FileName, MinimumSize->QuadPart, MaximumSize->QuadPart);
371 
372     if (MmNumberOfPagingFiles >= MAX_PAGING_FILES)
373     {
374         return STATUS_TOO_MANY_PAGING_FILES;
375     }
376 
377     PreviousMode = ExGetPreviousMode();
378 
379     if (PreviousMode != KernelMode)
380     {
381         if (SeSinglePrivilegeCheck(SeCreatePagefilePrivilege, PreviousMode) != TRUE)
382         {
383             return STATUS_PRIVILEGE_NOT_HELD;
384         }
385 
386         _SEH2_TRY
387         {
388             SafeMinimumSize = ProbeForReadLargeInteger(MinimumSize);
389             SafeMaximumSize = ProbeForReadLargeInteger(MaximumSize);
390             PageFileName = ProbeForReadUnicodeString(FileName);
391         }
392         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
393         {
394             /* Return the exception code */
395             _SEH2_YIELD(return _SEH2_GetExceptionCode());
396         }
397         _SEH2_END;
398     }
399     else
400     {
401         SafeMinimumSize = *MinimumSize;
402         SafeMaximumSize = *MaximumSize;
403         PageFileName = *FileName;
404     }
405 
406     /*
407      * Pagefiles can't be larger than 4GB and of course
408      * the minimum should be smaller than the maximum.
409      */
410     // TODO: Actually validate the lower bound of these sizes!
411     if (0 != SafeMinimumSize.u.HighPart)
412     {
413         return STATUS_INVALID_PARAMETER_2;
414     }
415     if (0 != SafeMaximumSize.u.HighPart)
416     {
417         return STATUS_INVALID_PARAMETER_3;
418     }
419     if (SafeMaximumSize.u.LowPart < SafeMinimumSize.u.LowPart)
420     {
421         return STATUS_INVALID_PARAMETER_MIX;
422     }
423 
424     /* Validate the name length */
425     if ((PageFileName.Length == 0) ||
426         (PageFileName.Length > 128 * sizeof(WCHAR)))
427     {
428         return STATUS_OBJECT_NAME_INVALID;
429     }
430 
431     /* We don't care about any potential UNICODE_NULL */
432     PageFileName.MaximumLength = PageFileName.Length;
433     /* Allocate a buffer to keep the name copy */
434     Buffer = ExAllocatePoolWithTag(PagedPool, PageFileName.Length, TAG_MM);
435     if (Buffer == NULL)
436     {
437         return STATUS_INSUFFICIENT_RESOURCES;
438     }
439 
440     /* Copy the name */
441     if (PreviousMode != KernelMode)
442     {
443         _SEH2_TRY
444         {
445             ProbeForRead(PageFileName.Buffer, PageFileName.Length, sizeof(WCHAR));
446             RtlCopyMemory(Buffer, PageFileName.Buffer, PageFileName.Length);
447         }
448         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
449         {
450             ExFreePoolWithTag(Buffer, TAG_MM);
451 
452             /* Return the exception code */
453             _SEH2_YIELD(return _SEH2_GetExceptionCode());
454         }
455         _SEH2_END;
456     }
457     else
458     {
459         RtlCopyMemory(Buffer, PageFileName.Buffer, PageFileName.Length);
460     }
461 
462     /* Replace caller's buffer with ours */
463     PageFileName.Buffer = Buffer;
464 
465     /* Create the security descriptor for the page file */
466     Status = RtlCreateSecurityDescriptor(&SecurityDescriptor, SECURITY_DESCRIPTOR_REVISION);
467     if (!NT_SUCCESS(Status))
468     {
469         ExFreePoolWithTag(Buffer, TAG_MM);
470         return Status;
471     }
472 
473     /* Create the DACL: we will only allow two SIDs */
474     Count = sizeof(ACL) + (sizeof(ACE) + RtlLengthSid(SeLocalSystemSid)) +
475                           (sizeof(ACE) + RtlLengthSid(SeAliasAdminsSid));
476     Dacl = ExAllocatePoolWithTag(PagedPool, Count, 'lcaD');
477     if (Dacl == NULL)
478     {
479         ExFreePoolWithTag(Buffer, TAG_MM);
480         return STATUS_INSUFFICIENT_RESOURCES;
481     }
482 
483     /* Initialize the DACL */
484     Status = RtlCreateAcl(Dacl, Count, ACL_REVISION);
485     if (!NT_SUCCESS(Status))
486     {
487         ExFreePoolWithTag(Dacl, 'lcaD');
488         ExFreePoolWithTag(Buffer, TAG_MM);
489         return Status;
490     }
491 
492     /* Grant full access to admins */
493     Status = RtlAddAccessAllowedAce(Dacl, ACL_REVISION, FILE_ALL_ACCESS, SeAliasAdminsSid);
494     if (!NT_SUCCESS(Status))
495     {
496         ExFreePoolWithTag(Dacl, 'lcaD');
497         ExFreePoolWithTag(Buffer, TAG_MM);
498         return Status;
499     }
500 
501     /* Grant full access to SYSTEM */
502     Status = RtlAddAccessAllowedAce(Dacl, ACL_REVISION, FILE_ALL_ACCESS, SeLocalSystemSid);
503     if (!NT_SUCCESS(Status))
504     {
505         ExFreePoolWithTag(Dacl, 'lcaD');
506         ExFreePoolWithTag(Buffer, TAG_MM);
507         return Status;
508     }
509 
510     /* Attach the DACL to the security descriptor */
511     Status = RtlSetDaclSecurityDescriptor(&SecurityDescriptor, TRUE, Dacl, FALSE);
512     if (!NT_SUCCESS(Status))
513     {
514         ExFreePoolWithTag(Dacl, 'lcaD');
515         ExFreePoolWithTag(Buffer, TAG_MM);
516         return Status;
517     }
518 
519     InitializeObjectAttributes(&ObjectAttributes,
520                                &PageFileName,
521                                OBJ_KERNEL_HANDLE,
522                                NULL,
523                                &SecurityDescriptor);
524 
525     /* Make sure we can at least store a complete page:
526      * If we have 2048 BytesPerAllocationUnit (FAT16 < 128MB) there is
527      * a problem if the paging file is fragmented. Suppose the first cluster
528      * of the paging file is cluster 3042 but cluster 3043 is NOT part of the
529      * paging file but of another file. We can't write a complete page (4096
530      * bytes) to the physical location of cluster 3042 then. */
531     AllocationSize.QuadPart = SafeMinimumSize.QuadPart + PAGE_SIZE;
532 
533     /* First, attempt to replace the page file, if existing */
534     Status = IoCreateFile(&FileHandle,
535                           SYNCHRONIZE | WRITE_DAC | FILE_READ_DATA | FILE_WRITE_DATA,
536                           &ObjectAttributes,
537                           &IoStatus,
538                           &AllocationSize,
539                           FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN,
540                           FILE_SHARE_WRITE,
541                           FILE_SUPERSEDE,
542                           FILE_DELETE_ON_CLOSE | FILE_NO_COMPRESSION | FILE_NO_INTERMEDIATE_BUFFERING,
543                           NULL,
544                           0,
545                           CreateFileTypeNone,
546                           NULL,
547                           SL_OPEN_PAGING_FILE | IO_NO_PARAMETER_CHECKING);
548     /* If we failed, relax a bit constraints, someone may be already holding the
549      * the file, so share write, don't attempt to replace and don't delete on close
550      * (basically, don't do anything conflicting)
551      * This can happen if the caller attempts to extend a page file.
552      */
553     if (!NT_SUCCESS(Status))
554     {
555         ULONG i;
556 
557         Status = IoCreateFile(&FileHandle,
558                               SYNCHRONIZE | FILE_WRITE_DATA,
559                               &ObjectAttributes,
560                               &IoStatus,
561                               &AllocationSize,
562                               FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN,
563                               FILE_SHARE_WRITE | FILE_SHARE_READ,
564                               FILE_OPEN,
565                               FILE_NO_COMPRESSION | FILE_NO_INTERMEDIATE_BUFFERING,
566                               NULL,
567                               0,
568                               CreateFileTypeNone,
569                               NULL,
570                               SL_OPEN_PAGING_FILE | IO_NO_PARAMETER_CHECKING);
571         if (!NT_SUCCESS(Status))
572         {
573             ExFreePoolWithTag(Dacl, 'lcaD');
574             ExFreePoolWithTag(Buffer, TAG_MM);
575             return Status;
576         }
577 
578         /* We opened it! Check we are that "someone" ;-)
579          * First, get the opened file object.
580          */
581         Status = ObReferenceObjectByHandle(FileHandle,
582                                            FILE_READ_DATA | FILE_WRITE_DATA,
583                                            IoFileObjectType,
584                                            KernelMode,
585                                            (PVOID*)&FileObject,
586                                            NULL);
587         if (!NT_SUCCESS(Status))
588         {
589             ZwClose(FileHandle);
590             ExFreePoolWithTag(Dacl, 'lcaD');
591             ExFreePoolWithTag(Buffer, TAG_MM);
592             return Status;
593         }
594 
595         /* Find if it matches a previous page file */
596         PagingFile = NULL;
597 
598         /* FIXME: should be calling unsafe instead,
599          * we should already be in a guarded region
600          */
601         KeAcquireGuardedMutex(&MmPageFileCreationLock);
602         if (MmNumberOfPagingFiles > 0)
603         {
604             i = 0;
605 
606             while (MmPagingFile[i]->FileObject->SectionObjectPointer != FileObject->SectionObjectPointer)
607             {
608                 ++i;
609                 if (i >= MmNumberOfPagingFiles)
610                 {
611                     break;
612                 }
613             }
614 
615             /* This is the matching page file */
616             PagingFile = MmPagingFile[i];
617         }
618 
619         /* If we didn't find the page file, fail */
620         if (PagingFile == NULL)
621         {
622             KeReleaseGuardedMutex(&MmPageFileCreationLock);
623             ObDereferenceObject(FileObject);
624             ZwClose(FileHandle);
625             ExFreePoolWithTag(Dacl, 'lcaD');
626             ExFreePoolWithTag(Buffer, TAG_MM);
627             return STATUS_NOT_FOUND;
628         }
629 
630         /* Don't allow page file shrinking */
631         if (PagingFile->MinimumSize > (SafeMinimumSize.QuadPart >> PAGE_SHIFT))
632         {
633             KeReleaseGuardedMutex(&MmPageFileCreationLock);
634             ObDereferenceObject(FileObject);
635             ZwClose(FileHandle);
636             ExFreePoolWithTag(Dacl, 'lcaD');
637             ExFreePoolWithTag(Buffer, TAG_MM);
638             return STATUS_INVALID_PARAMETER_2;
639         }
640 
641         if ((SafeMaximumSize.QuadPart >> PAGE_SHIFT) < PagingFile->MaximumSize)
642         {
643             KeReleaseGuardedMutex(&MmPageFileCreationLock);
644             ObDereferenceObject(FileObject);
645             ZwClose(FileHandle);
646             ExFreePoolWithTag(Dacl, 'lcaD');
647             ExFreePoolWithTag(Buffer, TAG_MM);
648             return STATUS_INVALID_PARAMETER_3;
649         }
650 
651         /* FIXME: implement parameters checking and page file extension */
652         UNIMPLEMENTED;
653 
654         KeReleaseGuardedMutex(&MmPageFileCreationLock);
655         ObDereferenceObject(FileObject);
656         ZwClose(FileHandle);
657         ExFreePoolWithTag(Dacl, 'lcaD');
658         ExFreePoolWithTag(Buffer, TAG_MM);
659         return STATUS_NOT_IMPLEMENTED;
660     }
661 
662     if (!NT_SUCCESS(Status))
663     {
664         DPRINT1("Failed creating page file: %lx\n", Status);
665         ExFreePoolWithTag(Dacl, 'lcaD');
666         ExFreePoolWithTag(Buffer, TAG_MM);
667         return Status;
668     }
669 
670     /* Set the security descriptor */
671     if (NT_SUCCESS(IoStatus.Status))
672     {
673         Status = ZwSetSecurityObject(FileHandle, DACL_SECURITY_INFORMATION, &SecurityDescriptor);
674         if (!NT_SUCCESS(Status))
675         {
676             ExFreePoolWithTag(Dacl, 'lcaD');
677             ZwClose(FileHandle);
678             ExFreePoolWithTag(Buffer, TAG_MM);
679             return Status;
680         }
681     }
682 
683     /* DACL is no longer needed, free it */
684     ExFreePoolWithTag(Dacl, 'lcaD');
685 
686     /* FIXME: To enable once page file managment is moved to ARM3 */
687 #if 0
688     /* Check we won't overflow commit limit with the page file */
689     if (MmTotalCommitLimitMaximum + (SafeMaximumSize.QuadPart >> PAGE_SHIFT) <= MmTotalCommitLimitMaximum)
690     {
691         ZwClose(FileHandle);
692         ExFreePoolWithTag(Buffer, TAG_MM);
693         return STATUS_INVALID_PARAMETER_3;
694     }
695 #endif
696 
697     /* Set its end of file to minimal size */
698     Status = ZwSetInformationFile(FileHandle,
699                                   &IoStatus,
700                                   &SafeMinimumSize,
701                                   sizeof(LARGE_INTEGER),
702                                   FileEndOfFileInformation);
703     if (!NT_SUCCESS(Status) || !NT_SUCCESS(IoStatus.Status))
704     {
705         ZwClose(FileHandle);
706         ExFreePoolWithTag(Buffer, TAG_MM);
707         return Status;
708     }
709 
710     Status = ObReferenceObjectByHandle(FileHandle,
711                                        FILE_READ_DATA | FILE_WRITE_DATA,
712                                        IoFileObjectType,
713                                        KernelMode,
714                                        (PVOID*)&FileObject,
715                                        NULL);
716     if (!NT_SUCCESS(Status))
717     {
718         ZwClose(FileHandle);
719         ExFreePoolWithTag(Buffer, TAG_MM);
720         return Status;
721     }
722 
723     /* Only allow page file on a few device types */
724     DeviceType = IoGetRelatedDeviceObject(FileObject)->DeviceType;
725     if (DeviceType != FILE_DEVICE_DISK_FILE_SYSTEM && DeviceType != FILE_DEVICE_NETWORK_FILE_SYSTEM &&
726         DeviceType != FILE_DEVICE_DFS_VOLUME && DeviceType != FILE_DEVICE_DFS_FILE_SYSTEM)
727     {
728         ObDereferenceObject(FileObject);
729         ZwClose(FileHandle);
730         ExFreePoolWithTag(Buffer, TAG_MM);
731         return Status;
732     }
733 
734     /* Deny page file creation on a floppy disk */
735     FsDeviceInfo.Characteristics = 0;
736     IoQueryVolumeInformation(FileObject, FileFsDeviceInformation, sizeof(FsDeviceInfo), &FsDeviceInfo, &Count);
737     if (BooleanFlagOn(FsDeviceInfo.Characteristics, FILE_FLOPPY_DISKETTE))
738     {
739         ObDereferenceObject(FileObject);
740         ZwClose(FileHandle);
741         ExFreePoolWithTag(Buffer, TAG_MM);
742         return STATUS_FLOPPY_VOLUME;
743     }
744 
745     PagingFile = ExAllocatePoolWithTag(NonPagedPool, sizeof(*PagingFile), TAG_MM);
746     if (PagingFile == NULL)
747     {
748         ObDereferenceObject(FileObject);
749         ZwClose(FileHandle);
750         ExFreePoolWithTag(Buffer, TAG_MM);
751         return STATUS_INSUFFICIENT_RESOURCES;
752     }
753 
754     RtlZeroMemory(PagingFile, sizeof(*PagingFile));
755 
756     PagingFile->FileHandle = FileHandle;
757     PagingFile->FileObject = FileObject;
758     PagingFile->MaximumSize = (SafeMaximumSize.QuadPart >> PAGE_SHIFT);
759     PagingFile->Size = (SafeMinimumSize.QuadPart >> PAGE_SHIFT);
760     PagingFile->MinimumSize = (SafeMinimumSize.QuadPart >> PAGE_SHIFT);
761     /* First page is never used: it's the header
762      * TODO: write it
763      */
764     PagingFile->FreeSpace = (ULONG)(SafeMinimumSize.QuadPart / PAGE_SIZE) - 1;
765     PagingFile->CurrentUsage = 0;
766     PagingFile->PageFileName = PageFileName;
767     ASSERT(PagingFile->Size == PagingFile->FreeSpace + PagingFile->CurrentUsage + 1);
768 
769     AllocMapSize = sizeof(RTL_BITMAP) + (((PagingFile->MaximumSize + 31) / 32) * sizeof(ULONG));
770     PagingFile->Bitmap = ExAllocatePoolWithTag(NonPagedPool,
771                                                AllocMapSize,
772                                                TAG_MM);
773     if (PagingFile->Bitmap == NULL)
774     {
775         ExFreePoolWithTag(PagingFile, TAG_MM);
776         ObDereferenceObject(FileObject);
777         ZwClose(FileHandle);
778         ExFreePoolWithTag(Buffer, TAG_MM);
779         return STATUS_INSUFFICIENT_RESOURCES;
780     }
781 
782     RtlInitializeBitMap(PagingFile->Bitmap,
783                         (PULONG)(PagingFile->Bitmap + 1),
784                         (ULONG)(PagingFile->MaximumSize));
785     RtlClearAllBits(PagingFile->Bitmap);
786 
787     /* FIXME: should be calling unsafe instead,
788      * we should already be in a guarded region
789      */
790     KeAcquireGuardedMutex(&MmPageFileCreationLock);
791     ASSERT(MmPagingFile[MmNumberOfPagingFiles] == NULL);
792     MmPagingFile[MmNumberOfPagingFiles] = PagingFile;
793     MmNumberOfPagingFiles++;
794     MiFreeSwapPages = MiFreeSwapPages + PagingFile->FreeSpace;
795     KeReleaseGuardedMutex(&MmPageFileCreationLock);
796 
797     MmSwapSpaceMessage = FALSE;
798 
799     if (!MmSystemPageFileLocated && BooleanFlagOn(FileObject->DeviceObject->Flags, DO_SYSTEM_BOOT_PARTITION))
800     {
801         MmSystemPageFileLocated = IoInitializeCrashDump(FileHandle);
802     }
803 
804     return STATUS_SUCCESS;
805 }
806 
807 /* EOF */
808