[NTOSKRNL] Add support for callback when enumerating large pool allocations
[reactos.git] / ntoskrnl / mm / pagefile.c
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 */