xref: /reactos/ntoskrnl/mm/pagefile.c (revision 8c2e9189)
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  * UPDATE HISTORY:
25  *                  Created 22/05/98
26  */
27 
28 /* INCLUDES *****************************************************************/
29 
30 #include <ntoskrnl.h>
31 #define NDEBUG
32 #include <debug.h>
33 
34 #if defined (ALLOC_PRAGMA)
35 #pragma alloc_text(INIT, MmInitPagingFile)
36 #endif
37 
38 PVOID
39 NTAPI
40 MiFindExportedRoutineByName(IN PVOID DllBase,
41                             IN PANSI_STRING ExportName);
42 
43 /* TYPES *********************************************************************/
44 
45 typedef struct _PAGINGFILE
46 {
47     LIST_ENTRY PagingFileListEntry;
48     PFILE_OBJECT FileObject;
49     LARGE_INTEGER MaximumSize;
50     LARGE_INTEGER CurrentSize;
51     PFN_NUMBER FreePages;
52     PFN_NUMBER UsedPages;
53     PULONG AllocMap;
54     KSPIN_LOCK AllocMapLock;
55     ULONG AllocMapSize;
56     PRETRIEVAL_POINTERS_BUFFER RetrievalPointers;
57 }
58 PAGINGFILE, *PPAGINGFILE;
59 
60 typedef struct _RETRIEVEL_DESCRIPTOR_LIST
61 {
62     struct _RETRIEVEL_DESCRIPTOR_LIST* Next;
63     RETRIEVAL_POINTERS_BUFFER RetrievalPointers;
64 }
65 RETRIEVEL_DESCRIPTOR_LIST, *PRETRIEVEL_DESCRIPTOR_LIST;
66 
67 /* GLOBALS *******************************************************************/
68 
69 #define PAIRS_PER_RUN (1024)
70 
71 #define MAX_PAGING_FILES  (16)
72 
73 /* List of paging files, both used and free */
74 static PPAGINGFILE PagingFileList[MAX_PAGING_FILES];
75 
76 /* Lock for examining the list of paging files */
77 static KSPIN_LOCK PagingFileListLock;
78 
79 /* Number of paging files */
80 ULONG MmNumberOfPagingFiles;
81 
82 /* Number of pages that are available for swapping */
83 PFN_COUNT MiFreeSwapPages;
84 
85 /* Number of pages that have been allocated for swapping */
86 PFN_COUNT MiUsedSwapPages;
87 
88 BOOLEAN MmZeroPageFile;
89 
90 /*
91  * Number of pages that have been reserved for swapping but not yet allocated
92  */
93 static PFN_COUNT MiReservedSwapPages;
94 
95 /*
96  * Ratio between reserved and available swap pages, e.g. setting this to five
97  * forces one swap page to be available for every five swap pages that are
98  * reserved. Setting this to zero turns off commit checking altogether.
99  */
100 #define MM_PAGEFILE_COMMIT_RATIO      (1)
101 
102 /*
103  * Number of pages that can be used for potentially swapable memory without
104  * pagefile space being reserved. The intention is that this allows smss
105  * to start up and create page files while ordinarily having a commit
106  * ratio of one.
107  */
108 #define MM_PAGEFILE_COMMIT_GRACE      (256)
109 
110 /*
111  * Translate between a swap entry and a file and offset pair.
112  */
113 #define FILE_FROM_ENTRY(i) ((i) & 0x0f)
114 #define OFFSET_FROM_ENTRY(i) ((i) >> 11)
115 #define ENTRY_FROM_FILE_OFFSET(i, j) ((i) | ((j) << 11) | 0x400)
116 
117 /* Make sure there can be only 16 paging files */
118 C_ASSERT(FILE_FROM_ENTRY(0xffffffff) < MAX_PAGING_FILES);
119 
120 static BOOLEAN MmSwapSpaceMessage = FALSE;
121 
122 /* FUNCTIONS *****************************************************************/
123 
124 VOID
125 NTAPI
126 MmBuildMdlFromPages(PMDL Mdl, PPFN_NUMBER Pages)
127 {
128     memcpy(Mdl + 1, Pages, sizeof(PFN_NUMBER) * (PAGE_ROUND_UP(Mdl->ByteOffset+Mdl->ByteCount)/PAGE_SIZE));
129 
130     /* FIXME: this flag should be set by the caller perhaps? */
131     Mdl->MdlFlags |= MDL_IO_PAGE_READ;
132 }
133 
134 
135 BOOLEAN
136 NTAPI
137 MmIsFileObjectAPagingFile(PFILE_OBJECT FileObject)
138 {
139     ULONG i;
140 
141     /* Loop through all the paging files */
142     for (i = 0; i < MmNumberOfPagingFiles; i++)
143     {
144         /* Check if this is one of them */
145         if (PagingFileList[i]->FileObject == FileObject) return TRUE;
146     }
147 
148     /* Nothing found */
149     return FALSE;
150 }
151 
152 VOID
153 NTAPI
154 MmShowOutOfSpaceMessagePagingFile(VOID)
155 {
156     if (!MmSwapSpaceMessage)
157     {
158         DPRINT1("MM: Out of swap space.\n");
159         MmSwapSpaceMessage = TRUE;
160     }
161 }
162 
163 static LARGE_INTEGER
164 MmGetOffsetPageFile(PRETRIEVAL_POINTERS_BUFFER RetrievalPointers, LARGE_INTEGER Offset)
165 {
166     /* Simple binary search */
167     ULONG first, last, mid;
168     first = 0;
169     last = RetrievalPointers->ExtentCount - 1;
170     while (first <= last)
171     {
172         mid = (last - first) / 2 + first;
173         if (Offset.QuadPart < RetrievalPointers->Extents[mid].NextVcn.QuadPart)
174         {
175             if (mid == 0)
176             {
177                 Offset.QuadPart += RetrievalPointers->Extents[0].Lcn.QuadPart - RetrievalPointers->StartingVcn.QuadPart;
178                 return Offset;
179             }
180             else
181             {
182                 if (Offset.QuadPart >= RetrievalPointers->Extents[mid-1].NextVcn.QuadPart)
183                 {
184                     Offset.QuadPart += RetrievalPointers->Extents[mid].Lcn.QuadPart - RetrievalPointers->Extents[mid-1].NextVcn.QuadPart;
185                     return Offset;
186                 }
187                 last = mid - 1;
188             }
189         }
190         else
191         {
192             if (mid == RetrievalPointers->ExtentCount - 1)
193             {
194                 break;
195             }
196             if (Offset.QuadPart < RetrievalPointers->Extents[mid+1].NextVcn.QuadPart)
197             {
198                 Offset.QuadPart += RetrievalPointers->Extents[mid+1].Lcn.QuadPart  - RetrievalPointers->Extents[mid].NextVcn.QuadPart;
199                 return Offset;
200             }
201             first = mid + 1;
202         }
203     }
204     KeBugCheck(MEMORY_MANAGEMENT);
205 #if defined(__GNUC__)
206 
207     return (LARGE_INTEGER)0LL;
208 #else
209 
210     {
211         const LARGE_INTEGER dummy =
212         {
213             {0}
214         };
215         return dummy;
216     }
217 #endif
218 }
219 
220 NTSTATUS
221 NTAPI
222 MmWriteToSwapPage(SWAPENTRY SwapEntry, PFN_NUMBER Page)
223 {
224     ULONG i;
225     ULONG_PTR offset;
226     LARGE_INTEGER file_offset;
227     IO_STATUS_BLOCK Iosb;
228     NTSTATUS Status;
229     KEVENT Event;
230     UCHAR MdlBase[sizeof(MDL) + sizeof(ULONG)];
231     PMDL Mdl = (PMDL)MdlBase;
232 
233     DPRINT("MmWriteToSwapPage\n");
234 
235     if (SwapEntry == 0)
236     {
237         KeBugCheck(MEMORY_MANAGEMENT);
238         return(STATUS_UNSUCCESSFUL);
239     }
240 
241     i = FILE_FROM_ENTRY(SwapEntry);
242     offset = OFFSET_FROM_ENTRY(SwapEntry) - 1;
243 
244     if (PagingFileList[i]->FileObject == NULL ||
245             PagingFileList[i]->FileObject->DeviceObject == NULL)
246     {
247         DPRINT1("Bad paging file 0x%.8X\n", SwapEntry);
248         KeBugCheck(MEMORY_MANAGEMENT);
249     }
250 
251     MmInitializeMdl(Mdl, NULL, PAGE_SIZE);
252     MmBuildMdlFromPages(Mdl, &Page);
253     Mdl->MdlFlags |= MDL_PAGES_LOCKED;
254 
255     file_offset.QuadPart = offset * PAGE_SIZE;
256     file_offset = MmGetOffsetPageFile(PagingFileList[i]->RetrievalPointers, file_offset);
257 
258     KeInitializeEvent(&Event, NotificationEvent, FALSE);
259     Status = IoSynchronousPageWrite(PagingFileList[i]->FileObject,
260                                     Mdl,
261                                     &file_offset,
262                                     &Event,
263                                     &Iosb);
264     if (Status == STATUS_PENDING)
265     {
266         KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
267         Status = Iosb.Status;
268     }
269 
270     if (Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA)
271     {
272         MmUnmapLockedPages (Mdl->MappedSystemVa, Mdl);
273     }
274     return(Status);
275 }
276 
277 
278 NTSTATUS
279 NTAPI
280 MmReadFromSwapPage(SWAPENTRY SwapEntry, PFN_NUMBER Page)
281 {
282     return MiReadPageFile(Page, FILE_FROM_ENTRY(SwapEntry), OFFSET_FROM_ENTRY(SwapEntry) - 1);
283 }
284 
285 NTSTATUS
286 NTAPI
287 MiReadPageFile(
288     _In_ PFN_NUMBER Page,
289     _In_ ULONG PageFileIndex,
290     _In_ ULONG_PTR PageFileOffset)
291 {
292     LARGE_INTEGER file_offset;
293     IO_STATUS_BLOCK Iosb;
294     NTSTATUS Status;
295     KEVENT Event;
296     UCHAR MdlBase[sizeof(MDL) + sizeof(ULONG)];
297     PMDL Mdl = (PMDL)MdlBase;
298     PPAGINGFILE PagingFile;
299 
300     DPRINT("MiReadSwapFile\n");
301 
302     if (PageFileOffset == 0)
303     {
304         KeBugCheck(MEMORY_MANAGEMENT);
305         return(STATUS_UNSUCCESSFUL);
306     }
307 
308     ASSERT(PageFileIndex < MAX_PAGING_FILES);
309 
310     PagingFile = PagingFileList[PageFileIndex];
311 
312     if (PagingFile->FileObject == NULL || PagingFile->FileObject->DeviceObject == NULL)
313     {
314         DPRINT1("Bad paging file %u\n", PageFileIndex);
315         KeBugCheck(MEMORY_MANAGEMENT);
316     }
317 
318     MmInitializeMdl(Mdl, NULL, PAGE_SIZE);
319     MmBuildMdlFromPages(Mdl, &Page);
320     Mdl->MdlFlags |= MDL_PAGES_LOCKED;
321 
322     file_offset.QuadPart = PageFileOffset * PAGE_SIZE;
323     file_offset = MmGetOffsetPageFile(PagingFile->RetrievalPointers, file_offset);
324 
325     KeInitializeEvent(&Event, NotificationEvent, FALSE);
326     Status = IoPageRead(PagingFile->FileObject,
327                         Mdl,
328                         &file_offset,
329                         &Event,
330                         &Iosb);
331     if (Status == STATUS_PENDING)
332     {
333         KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
334         Status = Iosb.Status;
335     }
336     if (Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA)
337     {
338         MmUnmapLockedPages (Mdl->MappedSystemVa, Mdl);
339     }
340     return(Status);
341 }
342 
343 VOID
344 INIT_FUNCTION
345 NTAPI
346 MmInitPagingFile(VOID)
347 {
348     ULONG i;
349 
350     KeInitializeSpinLock(&PagingFileListLock);
351 
352     MiFreeSwapPages = 0;
353     MiUsedSwapPages = 0;
354     MiReservedSwapPages = 0;
355 
356     for (i = 0; i < MAX_PAGING_FILES; i++)
357     {
358         PagingFileList[i] = NULL;
359     }
360     MmNumberOfPagingFiles = 0;
361 }
362 
363 static ULONG
364 MiAllocPageFromPagingFile(PPAGINGFILE PagingFile)
365 {
366     KIRQL oldIrql;
367     ULONG i, j;
368 
369     KeAcquireSpinLock(&PagingFile->AllocMapLock, &oldIrql);
370 
371     for (i = 0; i < PagingFile->AllocMapSize; i++)
372     {
373         for (j = 0; j < 32; j++)
374         {
375             if (!(PagingFile->AllocMap[i] & (1 << j)))
376             {
377                 PagingFile->AllocMap[i] |= (1 << j);
378                 PagingFile->UsedPages++;
379                 PagingFile->FreePages--;
380                 KeReleaseSpinLock(&PagingFile->AllocMapLock, oldIrql);
381                 return((i * 32) + j);
382             }
383         }
384     }
385 
386     KeReleaseSpinLock(&PagingFile->AllocMapLock, oldIrql);
387     return(0xFFFFFFFF);
388 }
389 
390 VOID
391 NTAPI
392 MmFreeSwapPage(SWAPENTRY Entry)
393 {
394     ULONG i;
395     ULONG_PTR off;
396     KIRQL oldIrql;
397 
398     i = FILE_FROM_ENTRY(Entry);
399     off = OFFSET_FROM_ENTRY(Entry) - 1;
400 
401     KeAcquireSpinLock(&PagingFileListLock, &oldIrql);
402     if (PagingFileList[i] == NULL)
403     {
404         KeBugCheck(MEMORY_MANAGEMENT);
405     }
406     KeAcquireSpinLockAtDpcLevel(&PagingFileList[i]->AllocMapLock);
407 
408     PagingFileList[i]->AllocMap[off >> 5] &= (~(1 << (off % 32)));
409 
410     PagingFileList[i]->FreePages++;
411     PagingFileList[i]->UsedPages--;
412 
413     MiFreeSwapPages++;
414     MiUsedSwapPages--;
415 
416     KeReleaseSpinLockFromDpcLevel(&PagingFileList[i]->AllocMapLock);
417     KeReleaseSpinLock(&PagingFileListLock, oldIrql);
418 }
419 
420 SWAPENTRY
421 NTAPI
422 MmAllocSwapPage(VOID)
423 {
424     KIRQL oldIrql;
425     ULONG i;
426     ULONG off;
427     SWAPENTRY entry;
428 
429     KeAcquireSpinLock(&PagingFileListLock, &oldIrql);
430 
431     if (MiFreeSwapPages == 0)
432     {
433         KeReleaseSpinLock(&PagingFileListLock, oldIrql);
434         return(0);
435     }
436 
437     for (i = 0; i < MAX_PAGING_FILES; i++)
438     {
439         if (PagingFileList[i] != NULL &&
440                 PagingFileList[i]->FreePages >= 1)
441         {
442             off = MiAllocPageFromPagingFile(PagingFileList[i]);
443             if (off == 0xFFFFFFFF)
444             {
445                 KeBugCheck(MEMORY_MANAGEMENT);
446                 KeReleaseSpinLock(&PagingFileListLock, oldIrql);
447                 return(STATUS_UNSUCCESSFUL);
448             }
449             MiUsedSwapPages++;
450             MiFreeSwapPages--;
451             KeReleaseSpinLock(&PagingFileListLock, oldIrql);
452 
453             entry = ENTRY_FROM_FILE_OFFSET(i, off + 1);
454             return(entry);
455         }
456     }
457 
458     KeReleaseSpinLock(&PagingFileListLock, oldIrql);
459     KeBugCheck(MEMORY_MANAGEMENT);
460     return(0);
461 }
462 
463 static PRETRIEVEL_DESCRIPTOR_LIST FASTCALL
464 MmAllocRetrievelDescriptorList(ULONG Pairs)
465 {
466     ULONG Size;
467     PRETRIEVEL_DESCRIPTOR_LIST RetDescList;
468 
469     Size = sizeof(RETRIEVEL_DESCRIPTOR_LIST) + Pairs * 2 * sizeof(LARGE_INTEGER);
470     RetDescList = ExAllocatePool(NonPagedPool, Size);
471     if (RetDescList)
472     {
473         RtlZeroMemory(RetDescList, Size);
474     }
475 
476     return RetDescList;
477 }
478 
479 NTSTATUS NTAPI
480 NtCreatePagingFile(IN PUNICODE_STRING FileName,
481                    IN PLARGE_INTEGER InitialSize,
482                    IN PLARGE_INTEGER MaximumSize,
483                    IN ULONG Reserved)
484 {
485     NTSTATUS Status;
486     OBJECT_ATTRIBUTES ObjectAttributes;
487     HANDLE FileHandle;
488     IO_STATUS_BLOCK IoStatus;
489     PFILE_OBJECT FileObject;
490     PPAGINGFILE PagingFile;
491     KIRQL oldIrql;
492     ULONG AllocMapSize;
493     FILE_FS_SIZE_INFORMATION FsSizeInformation;
494     PRETRIEVEL_DESCRIPTOR_LIST RetDescList;
495     PRETRIEVEL_DESCRIPTOR_LIST CurrentRetDescList;
496     ULONG i;
497     ULONG BytesPerAllocationUnit;
498     LARGE_INTEGER Vcn;
499     ULONG ExtentCount;
500     LARGE_INTEGER MaxVcn;
501     ULONG Count;
502     ULONG Size;
503     KPROCESSOR_MODE PreviousMode;
504     UNICODE_STRING CapturedFileName;
505     LARGE_INTEGER SafeInitialSize, SafeMaximumSize;
506 
507     DPRINT("NtCreatePagingFile(FileName %wZ, InitialSize %I64d)\n",
508            FileName, InitialSize->QuadPart);
509 
510     if (MmNumberOfPagingFiles >= MAX_PAGING_FILES)
511     {
512         return(STATUS_TOO_MANY_PAGING_FILES);
513     }
514 
515     PreviousMode = ExGetPreviousMode();
516 
517     if (PreviousMode != KernelMode)
518     {
519         _SEH2_TRY
520         {
521             SafeInitialSize = ProbeForReadLargeInteger(InitialSize);
522             SafeMaximumSize = ProbeForReadLargeInteger(MaximumSize);
523         }
524         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
525         {
526             /* Return the exception code */
527             _SEH2_YIELD(return _SEH2_GetExceptionCode());
528         }
529         _SEH2_END;
530     }
531     else
532     {
533         SafeInitialSize = *InitialSize;
534         SafeMaximumSize = *MaximumSize;
535     }
536 
537     /* Pagefiles can't be larger than 4GB and ofcourse the minimum should be
538        smaller than the maximum */
539     if (0 != SafeInitialSize.u.HighPart)
540     {
541         return STATUS_INVALID_PARAMETER_2;
542     }
543     if (0 != SafeMaximumSize.u.HighPart)
544     {
545         return STATUS_INVALID_PARAMETER_3;
546     }
547     if (SafeMaximumSize.u.LowPart < SafeInitialSize.u.LowPart)
548     {
549         return STATUS_INVALID_PARAMETER_MIX;
550     }
551 
552     Status = ProbeAndCaptureUnicodeString(&CapturedFileName,
553                                           PreviousMode,
554                                           FileName);
555     if (!NT_SUCCESS(Status))
556     {
557         return(Status);
558     }
559 
560     InitializeObjectAttributes(&ObjectAttributes,
561                                &CapturedFileName,
562                                OBJ_KERNEL_HANDLE,
563                                NULL,
564                                NULL);
565 
566     Status = IoCreateFile(&FileHandle,
567                           FILE_ALL_ACCESS,
568                           &ObjectAttributes,
569                           &IoStatus,
570                           NULL,
571                           0,
572                           0,
573                           FILE_OPEN_IF,
574                           FILE_SYNCHRONOUS_IO_NONALERT,
575                           NULL,
576                           0,
577                           CreateFileTypeNone,
578                           NULL,
579                           SL_OPEN_PAGING_FILE | IO_NO_PARAMETER_CHECKING);
580 
581     ReleaseCapturedUnicodeString(&CapturedFileName,
582                                  PreviousMode);
583     if (!NT_SUCCESS(Status))
584     {
585         return(Status);
586     }
587 
588     Status = ZwQueryVolumeInformationFile(FileHandle,
589                                           &IoStatus,
590                                           &FsSizeInformation,
591                                           sizeof(FILE_FS_SIZE_INFORMATION),
592                                           FileFsSizeInformation);
593     if (!NT_SUCCESS(Status))
594     {
595         ZwClose(FileHandle);
596         return Status;
597     }
598 
599     BytesPerAllocationUnit = FsSizeInformation.SectorsPerAllocationUnit *
600                              FsSizeInformation.BytesPerSector;
601     /* FIXME: If we have 2048 BytesPerAllocationUnit (FAT16 < 128MB) there is
602      * a problem if the paging file is fragmented. Suppose the first cluster
603      * of the paging file is cluster 3042 but cluster 3043 is NOT part of the
604      * paging file but of another file. We can't write a complete page (4096
605      * bytes) to the physical location of cluster 3042 then. */
606     if (BytesPerAllocationUnit % PAGE_SIZE)
607     {
608         DPRINT1("BytesPerAllocationUnit %lu is not a multiple of PAGE_SIZE %d\n",
609                 BytesPerAllocationUnit, PAGE_SIZE);
610         ZwClose(FileHandle);
611         return STATUS_UNSUCCESSFUL;
612     }
613 
614     Status = ZwSetInformationFile(FileHandle,
615                                   &IoStatus,
616                                   &SafeInitialSize,
617                                   sizeof(LARGE_INTEGER),
618                                   FileAllocationInformation);
619     if (!NT_SUCCESS(Status))
620     {
621         ZwClose(FileHandle);
622         return(Status);
623     }
624 
625     Status = ObReferenceObjectByHandle(FileHandle,
626                                        FILE_ALL_ACCESS,
627                                        IoFileObjectType,
628                                        KernelMode,
629                                        (PVOID*)&FileObject,
630                                        NULL);
631     if (!NT_SUCCESS(Status))
632     {
633         ZwClose(FileHandle);
634         return(Status);
635     }
636 
637     CurrentRetDescList = RetDescList = MmAllocRetrievelDescriptorList(PAIRS_PER_RUN);
638 
639     if (CurrentRetDescList == NULL)
640     {
641         ObDereferenceObject(FileObject);
642         ZwClose(FileHandle);
643         return(STATUS_NO_MEMORY);
644     }
645 
646 #if defined(__GNUC__)
647     Vcn.QuadPart = 0LL;
648 #else
649 
650     Vcn.QuadPart = 0;
651 #endif
652 
653     ExtentCount = 0;
654     MaxVcn.QuadPart = (SafeInitialSize.QuadPart + BytesPerAllocationUnit - 1) / BytesPerAllocationUnit;
655     while(1)
656     {
657         Status = ZwFsControlFile(FileHandle,
658                                  0,
659                                  NULL,
660                                  NULL,
661                                  &IoStatus,
662                                  FSCTL_GET_RETRIEVAL_POINTERS,
663                                  &Vcn,
664                                  sizeof(LARGE_INTEGER),
665                                  &CurrentRetDescList->RetrievalPointers,
666                                  sizeof(RETRIEVAL_POINTERS_BUFFER) + PAIRS_PER_RUN * 2 * sizeof(LARGE_INTEGER));
667         if (!NT_SUCCESS(Status))
668         {
669             while (RetDescList)
670             {
671                 CurrentRetDescList = RetDescList;
672                 RetDescList = RetDescList->Next;
673                 ExFreePool(CurrentRetDescList);
674             }
675             ObDereferenceObject(FileObject);
676             ZwClose(FileHandle);
677             return(Status);
678         }
679         ExtentCount += CurrentRetDescList->RetrievalPointers.ExtentCount;
680         if (CurrentRetDescList->RetrievalPointers.Extents[CurrentRetDescList->RetrievalPointers.ExtentCount-1].NextVcn.QuadPart < MaxVcn.QuadPart)
681         {
682             CurrentRetDescList->Next = MmAllocRetrievelDescriptorList(PAIRS_PER_RUN);
683             if (CurrentRetDescList->Next == NULL)
684             {
685                 while (RetDescList)
686                 {
687                     CurrentRetDescList = RetDescList;
688                     RetDescList = RetDescList->Next;
689                     ExFreePool(CurrentRetDescList);
690                 }
691                 ObDereferenceObject(FileObject);
692                 ZwClose(FileHandle);
693                 return(STATUS_NO_MEMORY);
694             }
695             Vcn = CurrentRetDescList->RetrievalPointers.Extents[CurrentRetDescList->RetrievalPointers.ExtentCount-1].NextVcn;
696             CurrentRetDescList = CurrentRetDescList->Next;
697         }
698         else
699         {
700             break;
701         }
702     }
703 
704     PagingFile = ExAllocatePool(NonPagedPool, sizeof(*PagingFile));
705     if (PagingFile == NULL)
706     {
707         while (RetDescList)
708         {
709             CurrentRetDescList = RetDescList;
710             RetDescList = RetDescList->Next;
711             ExFreePool(CurrentRetDescList);
712         }
713         ObDereferenceObject(FileObject);
714         ZwClose(FileHandle);
715         return(STATUS_NO_MEMORY);
716     }
717 
718     RtlZeroMemory(PagingFile, sizeof(*PagingFile));
719 
720     PagingFile->FileObject = FileObject;
721     PagingFile->MaximumSize.QuadPart = SafeMaximumSize.QuadPart;
722     PagingFile->CurrentSize.QuadPart = SafeInitialSize.QuadPart;
723     PagingFile->FreePages = (ULONG)(SafeInitialSize.QuadPart / PAGE_SIZE);
724     PagingFile->UsedPages = 0;
725     KeInitializeSpinLock(&PagingFile->AllocMapLock);
726 
727     AllocMapSize = (PagingFile->FreePages / 32) + 1;
728     PagingFile->AllocMap = ExAllocatePool(NonPagedPool,
729                                           AllocMapSize * sizeof(ULONG));
730     PagingFile->AllocMapSize = AllocMapSize;
731 
732     if (PagingFile->AllocMap == NULL)
733     {
734         while (RetDescList)
735         {
736             CurrentRetDescList = RetDescList;
737             RetDescList = RetDescList->Next;
738             ExFreePool(CurrentRetDescList);
739         }
740         ExFreePool(PagingFile);
741         ObDereferenceObject(FileObject);
742         ZwClose(FileHandle);
743         return(STATUS_NO_MEMORY);
744     }
745     DPRINT("ExtentCount: %lu\n", ExtentCount);
746     Size = sizeof(RETRIEVAL_POINTERS_BUFFER) + ExtentCount * 2 * sizeof(LARGE_INTEGER);
747     PagingFile->RetrievalPointers = ExAllocatePool(NonPagedPool, Size);
748     if (PagingFile->RetrievalPointers == NULL)
749     {
750         while (RetDescList)
751         {
752             CurrentRetDescList = RetDescList;
753             RetDescList = RetDescList->Next;
754             ExFreePool(CurrentRetDescList);
755         }
756         ExFreePool(PagingFile->AllocMap);
757         ExFreePool(PagingFile);
758         ObDereferenceObject(FileObject);
759         ZwClose(FileHandle);
760         return(STATUS_NO_MEMORY);
761     }
762 
763     RtlZeroMemory(PagingFile->AllocMap, AllocMapSize * sizeof(ULONG));
764     RtlZeroMemory(PagingFile->RetrievalPointers, Size);
765 
766     Count = 0;
767     PagingFile->RetrievalPointers->ExtentCount = ExtentCount;
768     PagingFile->RetrievalPointers->StartingVcn = RetDescList->RetrievalPointers.StartingVcn;
769     CurrentRetDescList = RetDescList;
770     while (CurrentRetDescList)
771     {
772         memcpy(&PagingFile->RetrievalPointers->Extents[Count],
773                CurrentRetDescList->RetrievalPointers.Extents,
774                CurrentRetDescList->RetrievalPointers.ExtentCount * 2 * sizeof(LARGE_INTEGER));
775         Count += CurrentRetDescList->RetrievalPointers.ExtentCount;
776         RetDescList = CurrentRetDescList;
777         CurrentRetDescList = CurrentRetDescList->Next;
778         ExFreePool(RetDescList);
779     }
780 
781     if (PagingFile->RetrievalPointers->ExtentCount != ExtentCount ||
782             PagingFile->RetrievalPointers->Extents[ExtentCount - 1].NextVcn.QuadPart != MaxVcn.QuadPart)
783     {
784         ExFreePool(PagingFile->RetrievalPointers);
785         ExFreePool(PagingFile->AllocMap);
786         ExFreePool(PagingFile);
787         ObDereferenceObject(FileObject);
788         ZwClose(FileHandle);
789         return(STATUS_UNSUCCESSFUL);
790     }
791 
792     /*
793      * Change the entries from lcn's to volume offset's.
794      */
795     PagingFile->RetrievalPointers->StartingVcn.QuadPart *= BytesPerAllocationUnit;
796     for (i = 0; i < ExtentCount; i++)
797     {
798         PagingFile->RetrievalPointers->Extents[i].Lcn.QuadPart *= BytesPerAllocationUnit;
799         PagingFile->RetrievalPointers->Extents[i].NextVcn.QuadPart *= BytesPerAllocationUnit;
800     }
801 
802     KeAcquireSpinLock(&PagingFileListLock, &oldIrql);
803     for (i = 0; i < MAX_PAGING_FILES; i++)
804     {
805         if (PagingFileList[i] == NULL)
806         {
807             PagingFileList[i] = PagingFile;
808             break;
809         }
810     }
811     MiFreeSwapPages = MiFreeSwapPages + PagingFile->FreePages;
812     MmNumberOfPagingFiles++;
813     KeReleaseSpinLock(&PagingFileListLock, oldIrql);
814 
815     ZwClose(FileHandle);
816 
817     MmSwapSpaceMessage = FALSE;
818 
819     return(STATUS_SUCCESS);
820 }
821 
822 /* EOF */
823