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