xref: /reactos/ntoskrnl/mm/pagefile.c (revision f080ee13)
1c2c66affSColin Finck /*
2c2c66affSColin Finck  *  ReactOS kernel
3c2c66affSColin Finck  *  Copyright (C) 1998, 1999, 2000, 2001 ReactOS Team
4c2c66affSColin Finck  *
5c2c66affSColin Finck  *  This program is free software; you can redistribute it and/or modify
6c2c66affSColin Finck  *  it under the terms of the GNU General Public License as published by
7c2c66affSColin Finck  *  the Free Software Foundation; either version 2 of the License, or
8c2c66affSColin Finck  *  (at your option) any later version.
9c2c66affSColin Finck  *
10c2c66affSColin Finck  *  This program is distributed in the hope that it will be useful,
11c2c66affSColin Finck  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12c2c66affSColin Finck  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13c2c66affSColin Finck  *  GNU General Public License for more details.
14c2c66affSColin Finck  *
15c2c66affSColin Finck  *  You should have received a copy of the GNU General Public License along
16c2c66affSColin Finck  *  with this program; if not, write to the Free Software Foundation, Inc.,
17c2c66affSColin Finck  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18c2c66affSColin Finck  */
19c2c66affSColin Finck /*
20c2c66affSColin Finck  * PROJECT:         ReactOS kernel
21c2c66affSColin Finck  * FILE:            ntoskrnl/mm/pagefile.c
22c2c66affSColin Finck  * PURPOSE:         Paging file functions
23c2c66affSColin Finck  * PROGRAMMER:      David Welch (welch@mcmail.com)
24c2c66affSColin Finck  * UPDATE HISTORY:
25c2c66affSColin Finck  *                  Created 22/05/98
26c2c66affSColin Finck  */
27c2c66affSColin Finck 
28c2c66affSColin Finck /* INCLUDES *****************************************************************/
29c2c66affSColin Finck 
30c2c66affSColin Finck #include <ntoskrnl.h>
31c2c66affSColin Finck #define NDEBUG
32c2c66affSColin Finck #include <debug.h>
33c2c66affSColin Finck 
34c2c66affSColin Finck #if defined (ALLOC_PRAGMA)
35c2c66affSColin Finck #pragma alloc_text(INIT, MmInitPagingFile)
36c2c66affSColin Finck #endif
37c2c66affSColin Finck 
38c2c66affSColin Finck PVOID
39c2c66affSColin Finck NTAPI
40c2c66affSColin Finck MiFindExportedRoutineByName(IN PVOID DllBase,
41c2c66affSColin Finck                             IN PANSI_STRING ExportName);
42c2c66affSColin Finck 
43c2c66affSColin Finck /* TYPES *********************************************************************/
44c2c66affSColin Finck 
45c2c66affSColin Finck typedef struct _PAGINGFILE
46c2c66affSColin Finck {
47c2c66affSColin Finck     PFILE_OBJECT FileObject;
482969c28aSPierre Schweitzer     HANDLE FileHandle;
49c2c66affSColin Finck     LARGE_INTEGER MaximumSize;
50c2c66affSColin Finck     LARGE_INTEGER CurrentSize;
51c2c66affSColin Finck     PFN_NUMBER FreePages;
52c2c66affSColin Finck     PFN_NUMBER UsedPages;
53*f080ee13SPierre Schweitzer     PRTL_BITMAP AllocMap;
54c2c66affSColin Finck     KSPIN_LOCK AllocMapLock;
55c2c66affSColin Finck }
56c2c66affSColin Finck PAGINGFILE, *PPAGINGFILE;
57c2c66affSColin Finck 
58c2c66affSColin Finck /* GLOBALS *******************************************************************/
59c2c66affSColin Finck 
60c2c66affSColin Finck #define PAIRS_PER_RUN (1024)
61c2c66affSColin Finck 
62c2c66affSColin Finck #define MAX_PAGING_FILES  (16)
63c2c66affSColin Finck 
64c2c66affSColin Finck /* List of paging files, both used and free */
65c2c66affSColin Finck static PPAGINGFILE PagingFileList[MAX_PAGING_FILES];
66c2c66affSColin Finck 
67c2c66affSColin Finck /* Lock for examining the list of paging files */
68c2c66affSColin Finck static KSPIN_LOCK PagingFileListLock;
69c2c66affSColin Finck 
70c2c66affSColin Finck /* Number of paging files */
71c2c66affSColin Finck ULONG MmNumberOfPagingFiles;
72c2c66affSColin Finck 
73c2c66affSColin Finck /* Number of pages that are available for swapping */
74c2c66affSColin Finck PFN_COUNT MiFreeSwapPages;
75c2c66affSColin Finck 
76c2c66affSColin Finck /* Number of pages that have been allocated for swapping */
77c2c66affSColin Finck PFN_COUNT MiUsedSwapPages;
78c2c66affSColin Finck 
79c2c66affSColin Finck BOOLEAN MmZeroPageFile;
80c2c66affSColin Finck 
81c2c66affSColin Finck /*
82c2c66affSColin Finck  * Number of pages that have been reserved for swapping but not yet allocated
83c2c66affSColin Finck  */
84c2c66affSColin Finck static PFN_COUNT MiReservedSwapPages;
85c2c66affSColin Finck 
86c2c66affSColin Finck /*
87c2c66affSColin Finck  * Ratio between reserved and available swap pages, e.g. setting this to five
88c2c66affSColin Finck  * forces one swap page to be available for every five swap pages that are
89c2c66affSColin Finck  * reserved. Setting this to zero turns off commit checking altogether.
90c2c66affSColin Finck  */
91c2c66affSColin Finck #define MM_PAGEFILE_COMMIT_RATIO      (1)
92c2c66affSColin Finck 
93c2c66affSColin Finck /*
94c2c66affSColin Finck  * Number of pages that can be used for potentially swapable memory without
95c2c66affSColin Finck  * pagefile space being reserved. The intention is that this allows smss
96c2c66affSColin Finck  * to start up and create page files while ordinarily having a commit
97c2c66affSColin Finck  * ratio of one.
98c2c66affSColin Finck  */
99c2c66affSColin Finck #define MM_PAGEFILE_COMMIT_GRACE      (256)
100c2c66affSColin Finck 
101c2c66affSColin Finck /*
102c2c66affSColin Finck  * Translate between a swap entry and a file and offset pair.
103c2c66affSColin Finck  */
104c2c66affSColin Finck #define FILE_FROM_ENTRY(i) ((i) & 0x0f)
105c2c66affSColin Finck #define OFFSET_FROM_ENTRY(i) ((i) >> 11)
106c2c66affSColin Finck #define ENTRY_FROM_FILE_OFFSET(i, j) ((i) | ((j) << 11) | 0x400)
107c2c66affSColin Finck 
108c2c66affSColin Finck /* Make sure there can be only 16 paging files */
109c2c66affSColin Finck C_ASSERT(FILE_FROM_ENTRY(0xffffffff) < MAX_PAGING_FILES);
110c2c66affSColin Finck 
111c2c66affSColin Finck static BOOLEAN MmSwapSpaceMessage = FALSE;
112c2c66affSColin Finck 
113c2c66affSColin Finck /* FUNCTIONS *****************************************************************/
114c2c66affSColin Finck 
115c2c66affSColin Finck VOID
116c2c66affSColin Finck NTAPI
117c2c66affSColin Finck MmBuildMdlFromPages(PMDL Mdl, PPFN_NUMBER Pages)
118c2c66affSColin Finck {
119c2c66affSColin Finck     memcpy(Mdl + 1, Pages, sizeof(PFN_NUMBER) * (PAGE_ROUND_UP(Mdl->ByteOffset+Mdl->ByteCount)/PAGE_SIZE));
120c2c66affSColin Finck 
121c2c66affSColin Finck     /* FIXME: this flag should be set by the caller perhaps? */
122c2c66affSColin Finck     Mdl->MdlFlags |= MDL_IO_PAGE_READ;
123c2c66affSColin Finck }
124c2c66affSColin Finck 
125c2c66affSColin Finck 
126c2c66affSColin Finck BOOLEAN
127c2c66affSColin Finck NTAPI
128c2c66affSColin Finck MmIsFileObjectAPagingFile(PFILE_OBJECT FileObject)
129c2c66affSColin Finck {
130c2c66affSColin Finck     ULONG i;
131c2c66affSColin Finck 
132c2c66affSColin Finck     /* Loop through all the paging files */
133c2c66affSColin Finck     for (i = 0; i < MmNumberOfPagingFiles; i++)
134c2c66affSColin Finck     {
135c2c66affSColin Finck         /* Check if this is one of them */
136c2c66affSColin Finck         if (PagingFileList[i]->FileObject == FileObject) return TRUE;
137c2c66affSColin Finck     }
138c2c66affSColin Finck 
139c2c66affSColin Finck     /* Nothing found */
140c2c66affSColin Finck     return FALSE;
141c2c66affSColin Finck }
142c2c66affSColin Finck 
143c2c66affSColin Finck VOID
144c2c66affSColin Finck NTAPI
145c2c66affSColin Finck MmShowOutOfSpaceMessagePagingFile(VOID)
146c2c66affSColin Finck {
147c2c66affSColin Finck     if (!MmSwapSpaceMessage)
148c2c66affSColin Finck     {
149c2c66affSColin Finck         DPRINT1("MM: Out of swap space.\n");
150c2c66affSColin Finck         MmSwapSpaceMessage = TRUE;
151c2c66affSColin Finck     }
152c2c66affSColin Finck }
153c2c66affSColin Finck 
154c2c66affSColin Finck NTSTATUS
155c2c66affSColin Finck NTAPI
156c2c66affSColin Finck MmWriteToSwapPage(SWAPENTRY SwapEntry, PFN_NUMBER Page)
157c2c66affSColin Finck {
158c2c66affSColin Finck     ULONG i;
159c2c66affSColin Finck     ULONG_PTR offset;
160c2c66affSColin Finck     LARGE_INTEGER file_offset;
161c2c66affSColin Finck     IO_STATUS_BLOCK Iosb;
162c2c66affSColin Finck     NTSTATUS Status;
163c2c66affSColin Finck     KEVENT Event;
164c2c66affSColin Finck     UCHAR MdlBase[sizeof(MDL) + sizeof(ULONG)];
165c2c66affSColin Finck     PMDL Mdl = (PMDL)MdlBase;
166c2c66affSColin Finck 
167c2c66affSColin Finck     DPRINT("MmWriteToSwapPage\n");
168c2c66affSColin Finck 
169c2c66affSColin Finck     if (SwapEntry == 0)
170c2c66affSColin Finck     {
171c2c66affSColin Finck         KeBugCheck(MEMORY_MANAGEMENT);
172c2c66affSColin Finck         return(STATUS_UNSUCCESSFUL);
173c2c66affSColin Finck     }
174c2c66affSColin Finck 
175c2c66affSColin Finck     i = FILE_FROM_ENTRY(SwapEntry);
176c2c66affSColin Finck     offset = OFFSET_FROM_ENTRY(SwapEntry) - 1;
177c2c66affSColin Finck 
178c2c66affSColin Finck     if (PagingFileList[i]->FileObject == NULL ||
179c2c66affSColin Finck             PagingFileList[i]->FileObject->DeviceObject == NULL)
180c2c66affSColin Finck     {
181c2c66affSColin Finck         DPRINT1("Bad paging file 0x%.8X\n", SwapEntry);
182c2c66affSColin Finck         KeBugCheck(MEMORY_MANAGEMENT);
183c2c66affSColin Finck     }
184c2c66affSColin Finck 
185c2c66affSColin Finck     MmInitializeMdl(Mdl, NULL, PAGE_SIZE);
186c2c66affSColin Finck     MmBuildMdlFromPages(Mdl, &Page);
187c2c66affSColin Finck     Mdl->MdlFlags |= MDL_PAGES_LOCKED;
188c2c66affSColin Finck 
189c2c66affSColin Finck     file_offset.QuadPart = offset * PAGE_SIZE;
190c2c66affSColin Finck 
191c2c66affSColin Finck     KeInitializeEvent(&Event, NotificationEvent, FALSE);
192c2c66affSColin Finck     Status = IoSynchronousPageWrite(PagingFileList[i]->FileObject,
193c2c66affSColin Finck                                     Mdl,
194c2c66affSColin Finck                                     &file_offset,
195c2c66affSColin Finck                                     &Event,
196c2c66affSColin Finck                                     &Iosb);
197c2c66affSColin Finck     if (Status == STATUS_PENDING)
198c2c66affSColin Finck     {
199c2c66affSColin Finck         KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
200c2c66affSColin Finck         Status = Iosb.Status;
201c2c66affSColin Finck     }
202c2c66affSColin Finck 
203c2c66affSColin Finck     if (Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA)
204c2c66affSColin Finck     {
205c2c66affSColin Finck         MmUnmapLockedPages (Mdl->MappedSystemVa, Mdl);
206c2c66affSColin Finck     }
207c2c66affSColin Finck     return(Status);
208c2c66affSColin Finck }
209c2c66affSColin Finck 
210c2c66affSColin Finck 
211c2c66affSColin Finck NTSTATUS
212c2c66affSColin Finck NTAPI
213c2c66affSColin Finck MmReadFromSwapPage(SWAPENTRY SwapEntry, PFN_NUMBER Page)
214c2c66affSColin Finck {
215c2c66affSColin Finck     return MiReadPageFile(Page, FILE_FROM_ENTRY(SwapEntry), OFFSET_FROM_ENTRY(SwapEntry) - 1);
216c2c66affSColin Finck }
217c2c66affSColin Finck 
218c2c66affSColin Finck NTSTATUS
219c2c66affSColin Finck NTAPI
220c2c66affSColin Finck MiReadPageFile(
221c2c66affSColin Finck     _In_ PFN_NUMBER Page,
222c2c66affSColin Finck     _In_ ULONG PageFileIndex,
223c2c66affSColin Finck     _In_ ULONG_PTR PageFileOffset)
224c2c66affSColin Finck {
225c2c66affSColin Finck     LARGE_INTEGER file_offset;
226c2c66affSColin Finck     IO_STATUS_BLOCK Iosb;
227c2c66affSColin Finck     NTSTATUS Status;
228c2c66affSColin Finck     KEVENT Event;
229c2c66affSColin Finck     UCHAR MdlBase[sizeof(MDL) + sizeof(ULONG)];
230c2c66affSColin Finck     PMDL Mdl = (PMDL)MdlBase;
231c2c66affSColin Finck     PPAGINGFILE PagingFile;
232c2c66affSColin Finck 
233c2c66affSColin Finck     DPRINT("MiReadSwapFile\n");
234c2c66affSColin Finck 
235c2c66affSColin Finck     if (PageFileOffset == 0)
236c2c66affSColin Finck     {
237c2c66affSColin Finck         KeBugCheck(MEMORY_MANAGEMENT);
238c2c66affSColin Finck         return(STATUS_UNSUCCESSFUL);
239c2c66affSColin Finck     }
240c2c66affSColin Finck 
241c2c66affSColin Finck     ASSERT(PageFileIndex < MAX_PAGING_FILES);
242c2c66affSColin Finck 
243c2c66affSColin Finck     PagingFile = PagingFileList[PageFileIndex];
244c2c66affSColin Finck 
245c2c66affSColin Finck     if (PagingFile->FileObject == NULL || PagingFile->FileObject->DeviceObject == NULL)
246c2c66affSColin Finck     {
247c2c66affSColin Finck         DPRINT1("Bad paging file %u\n", PageFileIndex);
248c2c66affSColin Finck         KeBugCheck(MEMORY_MANAGEMENT);
249c2c66affSColin Finck     }
250c2c66affSColin Finck 
251c2c66affSColin Finck     MmInitializeMdl(Mdl, NULL, PAGE_SIZE);
252c2c66affSColin Finck     MmBuildMdlFromPages(Mdl, &Page);
253c2c66affSColin Finck     Mdl->MdlFlags |= MDL_PAGES_LOCKED;
254c2c66affSColin Finck 
255c2c66affSColin Finck     file_offset.QuadPart = PageFileOffset * PAGE_SIZE;
256c2c66affSColin Finck 
257c2c66affSColin Finck     KeInitializeEvent(&Event, NotificationEvent, FALSE);
258c2c66affSColin Finck     Status = IoPageRead(PagingFile->FileObject,
259c2c66affSColin Finck                         Mdl,
260c2c66affSColin Finck                         &file_offset,
261c2c66affSColin Finck                         &Event,
262c2c66affSColin Finck                         &Iosb);
263c2c66affSColin Finck     if (Status == STATUS_PENDING)
264c2c66affSColin Finck     {
265c2c66affSColin Finck         KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
266c2c66affSColin Finck         Status = Iosb.Status;
267c2c66affSColin Finck     }
268c2c66affSColin Finck     if (Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA)
269c2c66affSColin Finck     {
270c2c66affSColin Finck         MmUnmapLockedPages (Mdl->MappedSystemVa, Mdl);
271c2c66affSColin Finck     }
272c2c66affSColin Finck     return(Status);
273c2c66affSColin Finck }
274c2c66affSColin Finck 
275c2c66affSColin Finck VOID
276c2c66affSColin Finck INIT_FUNCTION
277c2c66affSColin Finck NTAPI
278c2c66affSColin Finck MmInitPagingFile(VOID)
279c2c66affSColin Finck {
280c2c66affSColin Finck     ULONG i;
281c2c66affSColin Finck 
282c2c66affSColin Finck     KeInitializeSpinLock(&PagingFileListLock);
283c2c66affSColin Finck 
284c2c66affSColin Finck     MiFreeSwapPages = 0;
285c2c66affSColin Finck     MiUsedSwapPages = 0;
286c2c66affSColin Finck     MiReservedSwapPages = 0;
287c2c66affSColin Finck 
288c2c66affSColin Finck     for (i = 0; i < MAX_PAGING_FILES; i++)
289c2c66affSColin Finck     {
290c2c66affSColin Finck         PagingFileList[i] = NULL;
291c2c66affSColin Finck     }
292c2c66affSColin Finck     MmNumberOfPagingFiles = 0;
293c2c66affSColin Finck }
294c2c66affSColin Finck 
295c2c66affSColin Finck static ULONG
296c2c66affSColin Finck MiAllocPageFromPagingFile(PPAGINGFILE PagingFile)
297c2c66affSColin Finck {
298c2c66affSColin Finck     KIRQL oldIrql;
299*f080ee13SPierre Schweitzer     ULONG off;
300c2c66affSColin Finck 
301c2c66affSColin Finck     KeAcquireSpinLock(&PagingFile->AllocMapLock, &oldIrql);
302*f080ee13SPierre Schweitzer     off = RtlFindClearBitsAndSet(PagingFile->AllocMap, 1, 0);
303c2c66affSColin Finck     KeReleaseSpinLock(&PagingFile->AllocMapLock, oldIrql);
304c2c66affSColin Finck 
305*f080ee13SPierre Schweitzer     return off;
306c2c66affSColin Finck }
307c2c66affSColin Finck 
308c2c66affSColin Finck VOID
309c2c66affSColin Finck NTAPI
310c2c66affSColin Finck MmFreeSwapPage(SWAPENTRY Entry)
311c2c66affSColin Finck {
312c2c66affSColin Finck     ULONG i;
313c2c66affSColin Finck     ULONG_PTR off;
314c2c66affSColin Finck     KIRQL oldIrql;
315c2c66affSColin Finck 
316c2c66affSColin Finck     i = FILE_FROM_ENTRY(Entry);
317c2c66affSColin Finck     off = OFFSET_FROM_ENTRY(Entry) - 1;
318c2c66affSColin Finck 
319c2c66affSColin Finck     KeAcquireSpinLock(&PagingFileListLock, &oldIrql);
320c2c66affSColin Finck     if (PagingFileList[i] == NULL)
321c2c66affSColin Finck     {
322c2c66affSColin Finck         KeBugCheck(MEMORY_MANAGEMENT);
323c2c66affSColin Finck     }
324c2c66affSColin Finck     KeAcquireSpinLockAtDpcLevel(&PagingFileList[i]->AllocMapLock);
325c2c66affSColin Finck 
326*f080ee13SPierre Schweitzer     RtlClearBit(PagingFileList[i]->AllocMap, off >> 5);
327c2c66affSColin Finck 
328c2c66affSColin Finck     PagingFileList[i]->FreePages++;
329c2c66affSColin Finck     PagingFileList[i]->UsedPages--;
330c2c66affSColin Finck 
331c2c66affSColin Finck     MiFreeSwapPages++;
332c2c66affSColin Finck     MiUsedSwapPages--;
333c2c66affSColin Finck 
334c2c66affSColin Finck     KeReleaseSpinLockFromDpcLevel(&PagingFileList[i]->AllocMapLock);
335c2c66affSColin Finck     KeReleaseSpinLock(&PagingFileListLock, oldIrql);
336c2c66affSColin Finck }
337c2c66affSColin Finck 
338c2c66affSColin Finck SWAPENTRY
339c2c66affSColin Finck NTAPI
340c2c66affSColin Finck MmAllocSwapPage(VOID)
341c2c66affSColin Finck {
342c2c66affSColin Finck     KIRQL oldIrql;
343c2c66affSColin Finck     ULONG i;
344c2c66affSColin Finck     ULONG off;
345c2c66affSColin Finck     SWAPENTRY entry;
346c2c66affSColin Finck 
347c2c66affSColin Finck     KeAcquireSpinLock(&PagingFileListLock, &oldIrql);
348c2c66affSColin Finck 
349c2c66affSColin Finck     if (MiFreeSwapPages == 0)
350c2c66affSColin Finck     {
351c2c66affSColin Finck         KeReleaseSpinLock(&PagingFileListLock, oldIrql);
352c2c66affSColin Finck         return(0);
353c2c66affSColin Finck     }
354c2c66affSColin Finck 
355c2c66affSColin Finck     for (i = 0; i < MAX_PAGING_FILES; i++)
356c2c66affSColin Finck     {
357c2c66affSColin Finck         if (PagingFileList[i] != NULL &&
358c2c66affSColin Finck                 PagingFileList[i]->FreePages >= 1)
359c2c66affSColin Finck         {
360c2c66affSColin Finck             off = MiAllocPageFromPagingFile(PagingFileList[i]);
361c2c66affSColin Finck             if (off == 0xFFFFFFFF)
362c2c66affSColin Finck             {
363c2c66affSColin Finck                 KeBugCheck(MEMORY_MANAGEMENT);
364c2c66affSColin Finck                 KeReleaseSpinLock(&PagingFileListLock, oldIrql);
365c2c66affSColin Finck                 return(STATUS_UNSUCCESSFUL);
366c2c66affSColin Finck             }
367c2c66affSColin Finck             MiUsedSwapPages++;
368c2c66affSColin Finck             MiFreeSwapPages--;
369c2c66affSColin Finck             KeReleaseSpinLock(&PagingFileListLock, oldIrql);
370c2c66affSColin Finck 
371c2c66affSColin Finck             entry = ENTRY_FROM_FILE_OFFSET(i, off + 1);
372c2c66affSColin Finck             return(entry);
373c2c66affSColin Finck         }
374c2c66affSColin Finck     }
375c2c66affSColin Finck 
376c2c66affSColin Finck     KeReleaseSpinLock(&PagingFileListLock, oldIrql);
377c2c66affSColin Finck     KeBugCheck(MEMORY_MANAGEMENT);
378c2c66affSColin Finck     return(0);
379c2c66affSColin Finck }
380c2c66affSColin Finck 
381c2c66affSColin Finck NTSTATUS NTAPI
382c2c66affSColin Finck NtCreatePagingFile(IN PUNICODE_STRING FileName,
383c2c66affSColin Finck                    IN PLARGE_INTEGER InitialSize,
384c2c66affSColin Finck                    IN PLARGE_INTEGER MaximumSize,
385c2c66affSColin Finck                    IN ULONG Reserved)
386c2c66affSColin Finck {
387c2c66affSColin Finck     NTSTATUS Status;
388c2c66affSColin Finck     OBJECT_ATTRIBUTES ObjectAttributes;
389c2c66affSColin Finck     HANDLE FileHandle;
390c2c66affSColin Finck     IO_STATUS_BLOCK IoStatus;
391c2c66affSColin Finck     PFILE_OBJECT FileObject;
392c2c66affSColin Finck     PPAGINGFILE PagingFile;
393c2c66affSColin Finck     KIRQL oldIrql;
394c2c66affSColin Finck     ULONG AllocMapSize;
395c2c66affSColin Finck     ULONG i;
396c2c66affSColin Finck     ULONG Count;
397c2c66affSColin Finck     KPROCESSOR_MODE PreviousMode;
398c2c66affSColin Finck     UNICODE_STRING CapturedFileName;
3992969c28aSPierre Schweitzer     LARGE_INTEGER SafeInitialSize, SafeMaximumSize, AllocationSize;
40036c20dc5SPierre Schweitzer     FILE_FS_DEVICE_INFORMATION FsDeviceInfo;
40128b4b419SPierre Schweitzer     SECURITY_DESCRIPTOR SecurityDescriptor;
40228b4b419SPierre Schweitzer     PACL Dacl;
403c2c66affSColin Finck 
404c2c66affSColin Finck     DPRINT("NtCreatePagingFile(FileName %wZ, InitialSize %I64d)\n",
405c2c66affSColin Finck            FileName, InitialSize->QuadPart);
406c2c66affSColin Finck 
407c2c66affSColin Finck     if (MmNumberOfPagingFiles >= MAX_PAGING_FILES)
408c2c66affSColin Finck     {
4090ad4ef60SPierre Schweitzer         return STATUS_TOO_MANY_PAGING_FILES;
410c2c66affSColin Finck     }
411c2c66affSColin Finck 
412c2c66affSColin Finck     PreviousMode = ExGetPreviousMode();
413c2c66affSColin Finck 
414c2c66affSColin Finck     if (PreviousMode != KernelMode)
415c2c66affSColin Finck     {
4160ad4ef60SPierre Schweitzer         if (SeSinglePrivilegeCheck(SeCreatePagefilePrivilege, PreviousMode) != TRUE)
4170ad4ef60SPierre Schweitzer         {
4180ad4ef60SPierre Schweitzer             return STATUS_PRIVILEGE_NOT_HELD;
4190ad4ef60SPierre Schweitzer         }
4200ad4ef60SPierre Schweitzer 
421c2c66affSColin Finck         _SEH2_TRY
422c2c66affSColin Finck         {
423c2c66affSColin Finck             SafeInitialSize = ProbeForReadLargeInteger(InitialSize);
424c2c66affSColin Finck             SafeMaximumSize = ProbeForReadLargeInteger(MaximumSize);
425c2c66affSColin Finck         }
426c2c66affSColin Finck         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
427c2c66affSColin Finck         {
428c2c66affSColin Finck             /* Return the exception code */
429c2c66affSColin Finck             _SEH2_YIELD(return _SEH2_GetExceptionCode());
430c2c66affSColin Finck         }
431c2c66affSColin Finck         _SEH2_END;
432c2c66affSColin Finck     }
433c2c66affSColin Finck     else
434c2c66affSColin Finck     {
435c2c66affSColin Finck         SafeInitialSize = *InitialSize;
436c2c66affSColin Finck         SafeMaximumSize = *MaximumSize;
437c2c66affSColin Finck     }
438c2c66affSColin Finck 
439c2c66affSColin Finck     /* Pagefiles can't be larger than 4GB and ofcourse the minimum should be
440c2c66affSColin Finck        smaller than the maximum */
441c2c66affSColin Finck     if (0 != SafeInitialSize.u.HighPart)
442c2c66affSColin Finck     {
443c2c66affSColin Finck         return STATUS_INVALID_PARAMETER_2;
444c2c66affSColin Finck     }
445c2c66affSColin Finck     if (0 != SafeMaximumSize.u.HighPart)
446c2c66affSColin Finck     {
447c2c66affSColin Finck         return STATUS_INVALID_PARAMETER_3;
448c2c66affSColin Finck     }
449c2c66affSColin Finck     if (SafeMaximumSize.u.LowPart < SafeInitialSize.u.LowPart)
450c2c66affSColin Finck     {
451c2c66affSColin Finck         return STATUS_INVALID_PARAMETER_MIX;
452c2c66affSColin Finck     }
453c2c66affSColin Finck 
454c2c66affSColin Finck     Status = ProbeAndCaptureUnicodeString(&CapturedFileName,
455c2c66affSColin Finck                                           PreviousMode,
456c2c66affSColin Finck                                           FileName);
457c2c66affSColin Finck     if (!NT_SUCCESS(Status))
458c2c66affSColin Finck     {
459c2c66affSColin Finck         return(Status);
460c2c66affSColin Finck     }
461c2c66affSColin Finck 
46228b4b419SPierre Schweitzer     /* Create the security descriptor for the page file */
46328b4b419SPierre Schweitzer     Status = RtlCreateSecurityDescriptor(&SecurityDescriptor, SECURITY_DESCRIPTOR_REVISION);
46428b4b419SPierre Schweitzer     if (!NT_SUCCESS(Status))
46528b4b419SPierre Schweitzer     {
46628b4b419SPierre Schweitzer         ReleaseCapturedUnicodeString(&CapturedFileName,
46728b4b419SPierre Schweitzer                                      PreviousMode);
46828b4b419SPierre Schweitzer         return Status;
46928b4b419SPierre Schweitzer     }
47028b4b419SPierre Schweitzer 
47128b4b419SPierre Schweitzer     /* Create the DACL: we will only allow two SIDs */
47228b4b419SPierre Schweitzer     Count = sizeof(ACL) + (sizeof(ACE) + RtlLengthSid(SeLocalSystemSid)) +
47328b4b419SPierre Schweitzer                           (sizeof(ACE) + RtlLengthSid(SeAliasAdminsSid));
47428b4b419SPierre Schweitzer     Dacl = ExAllocatePoolWithTag(PagedPool, Count, 'lcaD');
47528b4b419SPierre Schweitzer     if (Dacl == NULL)
47628b4b419SPierre Schweitzer     {
47728b4b419SPierre Schweitzer         ReleaseCapturedUnicodeString(&CapturedFileName,
47828b4b419SPierre Schweitzer                                      PreviousMode);
47928b4b419SPierre Schweitzer         return STATUS_INSUFFICIENT_RESOURCES;
48028b4b419SPierre Schweitzer     }
48128b4b419SPierre Schweitzer 
48228b4b419SPierre Schweitzer     /* Initialize the DACL */
48328b4b419SPierre Schweitzer     Status = RtlCreateAcl(Dacl, Count, ACL_REVISION);
48428b4b419SPierre Schweitzer     if (!NT_SUCCESS(Status))
48528b4b419SPierre Schweitzer     {
48628b4b419SPierre Schweitzer         ExFreePoolWithTag(Dacl, 'lcaD');
48728b4b419SPierre Schweitzer         ReleaseCapturedUnicodeString(&CapturedFileName,
48828b4b419SPierre Schweitzer                                      PreviousMode);
48928b4b419SPierre Schweitzer         return Status;
49028b4b419SPierre Schweitzer     }
49128b4b419SPierre Schweitzer 
49228b4b419SPierre Schweitzer     /* Grant full access to admins */
49328b4b419SPierre Schweitzer     Status = RtlAddAccessAllowedAce(Dacl, ACL_REVISION, FILE_ALL_ACCESS, SeAliasAdminsSid);
49428b4b419SPierre Schweitzer     if (!NT_SUCCESS(Status))
49528b4b419SPierre Schweitzer     {
49628b4b419SPierre Schweitzer         ExFreePoolWithTag(Dacl, 'lcaD');
49728b4b419SPierre Schweitzer         ReleaseCapturedUnicodeString(&CapturedFileName,
49828b4b419SPierre Schweitzer                                      PreviousMode);
49928b4b419SPierre Schweitzer         return Status;
50028b4b419SPierre Schweitzer     }
50128b4b419SPierre Schweitzer 
50228b4b419SPierre Schweitzer     /* Grant full access to SYSTEM */
50328b4b419SPierre Schweitzer     Status = RtlAddAccessAllowedAce(Dacl, ACL_REVISION, FILE_ALL_ACCESS, SeLocalSystemSid);
50428b4b419SPierre Schweitzer     if (!NT_SUCCESS(Status))
50528b4b419SPierre Schweitzer     {
50628b4b419SPierre Schweitzer         ExFreePoolWithTag(Dacl, 'lcaD');
50728b4b419SPierre Schweitzer         ReleaseCapturedUnicodeString(&CapturedFileName,
50828b4b419SPierre Schweitzer                                      PreviousMode);
50928b4b419SPierre Schweitzer         return Status;
51028b4b419SPierre Schweitzer     }
51128b4b419SPierre Schweitzer 
51228b4b419SPierre Schweitzer     /* Attach the DACL to the security descriptor */
51328b4b419SPierre Schweitzer     Status = RtlSetDaclSecurityDescriptor(&SecurityDescriptor, TRUE, Dacl, FALSE);
51428b4b419SPierre Schweitzer     if (!NT_SUCCESS(Status))
51528b4b419SPierre Schweitzer     {
51628b4b419SPierre Schweitzer         ExFreePoolWithTag(Dacl, 'lcaD');
51728b4b419SPierre Schweitzer         ReleaseCapturedUnicodeString(&CapturedFileName,
51828b4b419SPierre Schweitzer                                      PreviousMode);
51928b4b419SPierre Schweitzer         return Status;
52028b4b419SPierre Schweitzer     }
52128b4b419SPierre Schweitzer 
522c2c66affSColin Finck     InitializeObjectAttributes(&ObjectAttributes,
523c2c66affSColin Finck                                &CapturedFileName,
524c2c66affSColin Finck                                OBJ_KERNEL_HANDLE,
525c2c66affSColin Finck                                NULL,
52628b4b419SPierre Schweitzer                                &SecurityDescriptor);
527c2c66affSColin Finck 
5282969c28aSPierre Schweitzer     /* Make sure we can at least store a complete page:
5292969c28aSPierre Schweitzer      * If we have 2048 BytesPerAllocationUnit (FAT16 < 128MB) there is
5302969c28aSPierre Schweitzer      * a problem if the paging file is fragmented. Suppose the first cluster
5312969c28aSPierre Schweitzer      * of the paging file is cluster 3042 but cluster 3043 is NOT part of the
5322969c28aSPierre Schweitzer      * paging file but of another file. We can't write a complete page (4096
5332969c28aSPierre Schweitzer      * bytes) to the physical location of cluster 3042 then. */
5342969c28aSPierre Schweitzer     AllocationSize.QuadPart = SafeInitialSize.QuadPart + PAGE_SIZE;
5352969c28aSPierre Schweitzer 
5362969c28aSPierre Schweitzer     /* First, attempt to replace the page file, if existing */
537c2c66affSColin Finck     Status = IoCreateFile(&FileHandle,
5382969c28aSPierre Schweitzer                           SYNCHRONIZE | WRITE_DAC | FILE_READ_DATA | FILE_WRITE_DATA,
539c2c66affSColin Finck                           &ObjectAttributes,
540c2c66affSColin Finck                           &IoStatus,
5412969c28aSPierre Schweitzer                           &AllocationSize,
5422969c28aSPierre Schweitzer                           FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN,
5432969c28aSPierre Schweitzer                           FILE_SHARE_WRITE,
5442969c28aSPierre Schweitzer                           FILE_SUPERSEDE,
5452969c28aSPierre Schweitzer                           FILE_DELETE_ON_CLOSE | FILE_NO_COMPRESSION | FILE_NO_INTERMEDIATE_BUFFERING,
546c2c66affSColin Finck                           NULL,
547c2c66affSColin Finck                           0,
548c2c66affSColin Finck                           CreateFileTypeNone,
549c2c66affSColin Finck                           NULL,
550c2c66affSColin Finck                           SL_OPEN_PAGING_FILE | IO_NO_PARAMETER_CHECKING);
5512969c28aSPierre Schweitzer     /* If we failed, relax a bit constraints, someone may be already holding the
5522969c28aSPierre Schweitzer      * the file, so share write, don't attempt to replace and don't delete on close
5532969c28aSPierre Schweitzer      * (basically, don't do anything conflicting)
5542969c28aSPierre Schweitzer      */
5552969c28aSPierre Schweitzer     if (!NT_SUCCESS(Status))
5562969c28aSPierre Schweitzer     {
5572969c28aSPierre Schweitzer         Status = IoCreateFile(&FileHandle,
5582969c28aSPierre Schweitzer                               SYNCHRONIZE | FILE_WRITE_DATA,
5592969c28aSPierre Schweitzer                               &ObjectAttributes,
5602969c28aSPierre Schweitzer                               &IoStatus,
5612969c28aSPierre Schweitzer                               &AllocationSize,
5622969c28aSPierre Schweitzer                               FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN,
5632969c28aSPierre Schweitzer                               FILE_SHARE_WRITE | FILE_SHARE_READ,
5642969c28aSPierre Schweitzer                               FILE_OPEN,
5652969c28aSPierre Schweitzer                               FILE_NO_COMPRESSION | FILE_NO_INTERMEDIATE_BUFFERING,
5662969c28aSPierre Schweitzer                               NULL,
5672969c28aSPierre Schweitzer                               0,
5682969c28aSPierre Schweitzer                               CreateFileTypeNone,
5692969c28aSPierre Schweitzer                               NULL,
5702969c28aSPierre Schweitzer                               SL_OPEN_PAGING_FILE | IO_NO_PARAMETER_CHECKING);
5712969c28aSPierre Schweitzer     }
572c2c66affSColin Finck 
573c2c66affSColin Finck     ReleaseCapturedUnicodeString(&CapturedFileName,
574c2c66affSColin Finck                                  PreviousMode);
575c2c66affSColin Finck     if (!NT_SUCCESS(Status))
576c2c66affSColin Finck     {
5772969c28aSPierre Schweitzer         DPRINT1("Failed creating page file: %lx\n", Status);
57828b4b419SPierre Schweitzer         ExFreePoolWithTag(Dacl, 'lcaD');
579c2c66affSColin Finck         return(Status);
580c2c66affSColin Finck     }
581c2c66affSColin Finck 
58228b4b419SPierre Schweitzer     /* Set the security descriptor */
58328b4b419SPierre Schweitzer     if (NT_SUCCESS(IoStatus.Status))
58428b4b419SPierre Schweitzer     {
58528b4b419SPierre Schweitzer         Status = ZwSetSecurityObject(FileHandle, DACL_SECURITY_INFORMATION, &SecurityDescriptor);
58628b4b419SPierre Schweitzer         if (!NT_SUCCESS(Status))
58728b4b419SPierre Schweitzer         {
58828b4b419SPierre Schweitzer             ExFreePoolWithTag(Dacl, 'lcaD');
58928b4b419SPierre Schweitzer             ZwClose(FileHandle);
59028b4b419SPierre Schweitzer             return Status;
59128b4b419SPierre Schweitzer         }
59228b4b419SPierre Schweitzer     }
59328b4b419SPierre Schweitzer 
59428b4b419SPierre Schweitzer     /* DACL is no longer needed, free it */
59528b4b419SPierre Schweitzer     ExFreePoolWithTag(Dacl, 'lcaD');
59628b4b419SPierre Schweitzer 
5972969c28aSPierre Schweitzer     /* Set its end of file to initial size */
598c2c66affSColin Finck     Status = ZwSetInformationFile(FileHandle,
599c2c66affSColin Finck                                   &IoStatus,
600c2c66affSColin Finck                                   &SafeInitialSize,
601c2c66affSColin Finck                                   sizeof(LARGE_INTEGER),
6022969c28aSPierre Schweitzer                                   FileEndOfFileInformation);
6032969c28aSPierre Schweitzer     if (!NT_SUCCESS(Status) || !NT_SUCCESS(IoStatus.Status))
604c2c66affSColin Finck     {
605c2c66affSColin Finck         ZwClose(FileHandle);
606c2c66affSColin Finck         return(Status);
607c2c66affSColin Finck     }
608c2c66affSColin Finck 
609c2c66affSColin Finck     Status = ObReferenceObjectByHandle(FileHandle,
610c2c66affSColin Finck                                        FILE_ALL_ACCESS,
611c2c66affSColin Finck                                        IoFileObjectType,
612c2c66affSColin Finck                                        KernelMode,
613c2c66affSColin Finck                                        (PVOID*)&FileObject,
614c2c66affSColin Finck                                        NULL);
615c2c66affSColin Finck     if (!NT_SUCCESS(Status))
616c2c66affSColin Finck     {
617c2c66affSColin Finck         ZwClose(FileHandle);
618c2c66affSColin Finck         return(Status);
619c2c66affSColin Finck     }
620c2c66affSColin Finck 
62136c20dc5SPierre Schweitzer     /* Deny page file creation on a floppy disk */
62236c20dc5SPierre Schweitzer     FsDeviceInfo.Characteristics = 0;
62336c20dc5SPierre Schweitzer     IoQueryVolumeInformation(FileObject, FileFsDeviceInformation, sizeof(FsDeviceInfo), &FsDeviceInfo, &Count);
62436c20dc5SPierre Schweitzer     if (BooleanFlagOn(FsDeviceInfo.Characteristics, FILE_FLOPPY_DISKETTE))
62536c20dc5SPierre Schweitzer     {
62636c20dc5SPierre Schweitzer         ObDereferenceObject(FileObject);
62736c20dc5SPierre Schweitzer         ZwClose(FileHandle);
62836c20dc5SPierre Schweitzer         return STATUS_FLOPPY_VOLUME;
62936c20dc5SPierre Schweitzer     }
63036c20dc5SPierre Schweitzer 
631c2c66affSColin Finck     PagingFile = ExAllocatePool(NonPagedPool, sizeof(*PagingFile));
632c2c66affSColin Finck     if (PagingFile == NULL)
633c2c66affSColin Finck     {
634c2c66affSColin Finck         ObDereferenceObject(FileObject);
635c2c66affSColin Finck         ZwClose(FileHandle);
636c2c66affSColin Finck         return(STATUS_NO_MEMORY);
637c2c66affSColin Finck     }
638c2c66affSColin Finck 
639c2c66affSColin Finck     RtlZeroMemory(PagingFile, sizeof(*PagingFile));
640c2c66affSColin Finck 
6412969c28aSPierre Schweitzer     PagingFile->FileHandle = FileHandle;
642c2c66affSColin Finck     PagingFile->FileObject = FileObject;
643c2c66affSColin Finck     PagingFile->MaximumSize.QuadPart = SafeMaximumSize.QuadPart;
644c2c66affSColin Finck     PagingFile->CurrentSize.QuadPart = SafeInitialSize.QuadPart;
645c2c66affSColin Finck     PagingFile->FreePages = (ULONG)(SafeInitialSize.QuadPart / PAGE_SIZE);
646c2c66affSColin Finck     PagingFile->UsedPages = 0;
647c2c66affSColin Finck     KeInitializeSpinLock(&PagingFile->AllocMapLock);
648c2c66affSColin Finck 
649*f080ee13SPierre Schweitzer     AllocMapSize = sizeof(RTL_BITMAP) + (((PagingFile->FreePages + 31) / 32) * sizeof(ULONG));
650*f080ee13SPierre Schweitzer     PagingFile->AllocMap = ExAllocatePoolWithTag(NonPagedPool,
651*f080ee13SPierre Schweitzer                                                  AllocMapSize,
652*f080ee13SPierre Schweitzer                                                  TAG_MM);
653c2c66affSColin Finck     if (PagingFile->AllocMap == NULL)
654c2c66affSColin Finck     {
655c2c66affSColin Finck         ExFreePool(PagingFile);
656c2c66affSColin Finck         ObDereferenceObject(FileObject);
657c2c66affSColin Finck         ZwClose(FileHandle);
658c2c66affSColin Finck         return(STATUS_NO_MEMORY);
659c2c66affSColin Finck     }
660c2c66affSColin Finck 
661*f080ee13SPierre Schweitzer     RtlInitializeBitMap(PagingFile->AllocMap,
662*f080ee13SPierre Schweitzer                         (PULONG)(PagingFile->AllocMap + 1),
663*f080ee13SPierre Schweitzer                         (ULONG)(PagingFile->FreePages));
664*f080ee13SPierre Schweitzer     RtlClearAllBits(PagingFile->AllocMap);
665c2c66affSColin Finck 
666c2c66affSColin Finck     KeAcquireSpinLock(&PagingFileListLock, &oldIrql);
667c2c66affSColin Finck     for (i = 0; i < MAX_PAGING_FILES; i++)
668c2c66affSColin Finck     {
669c2c66affSColin Finck         if (PagingFileList[i] == NULL)
670c2c66affSColin Finck         {
671c2c66affSColin Finck             PagingFileList[i] = PagingFile;
672c2c66affSColin Finck             break;
673c2c66affSColin Finck         }
674c2c66affSColin Finck     }
675c2c66affSColin Finck     MiFreeSwapPages = MiFreeSwapPages + PagingFile->FreePages;
676c2c66affSColin Finck     MmNumberOfPagingFiles++;
677c2c66affSColin Finck     KeReleaseSpinLock(&PagingFileListLock, oldIrql);
678c2c66affSColin Finck 
679c2c66affSColin Finck     MmSwapSpaceMessage = FALSE;
680c2c66affSColin Finck 
681c2c66affSColin Finck     return(STATUS_SUCCESS);
682c2c66affSColin Finck }
683c2c66affSColin Finck 
684c2c66affSColin Finck /* EOF */
685