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