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