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