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