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