1984b01fdd61c90ecf983b3b5dc43516fd48f6b9
[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 = STATUS_SUCCESS;
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 Status = _SEH2_GetExceptionCode();
564 }
565 _SEH2_END;
566
567 if (!NT_SUCCESS(Status))
568 {
569 return Status;
570 }
571 }
572 else
573 {
574 SafeInitialSize = *InitialSize;
575 SafeMaximumSize = *MaximumSize;
576 }
577
578 /* Pagefiles can't be larger than 4GB and ofcourse the minimum should be
579 smaller than the maximum */
580 if (0 != SafeInitialSize.u.HighPart)
581 {
582 return STATUS_INVALID_PARAMETER_2;
583 }
584 if (0 != SafeMaximumSize.u.HighPart)
585 {
586 return STATUS_INVALID_PARAMETER_3;
587 }
588 if (SafeMaximumSize.u.LowPart < SafeInitialSize.u.LowPart)
589 {
590 return STATUS_INVALID_PARAMETER_MIX;
591 }
592
593 Status = ProbeAndCaptureUnicodeString(&CapturedFileName,
594 PreviousMode,
595 FileName);
596 if (!NT_SUCCESS(Status))
597 {
598 return(Status);
599 }
600
601 InitializeObjectAttributes(&ObjectAttributes,
602 &CapturedFileName,
603 0,
604 NULL,
605 NULL);
606
607 Status = IoCreateFile(&FileHandle,
608 FILE_ALL_ACCESS,
609 &ObjectAttributes,
610 &IoStatus,
611 NULL,
612 0,
613 0,
614 FILE_OPEN_IF,
615 FILE_SYNCHRONOUS_IO_NONALERT,
616 NULL,
617 0,
618 CreateFileTypeNone,
619 NULL,
620 SL_OPEN_PAGING_FILE | IO_NO_PARAMETER_CHECKING);
621
622 ReleaseCapturedUnicodeString(&CapturedFileName,
623 PreviousMode);
624 if (!NT_SUCCESS(Status))
625 {
626 return(Status);
627 }
628
629 Status = ZwQueryVolumeInformationFile(FileHandle,
630 &IoStatus,
631 &FsSizeInformation,
632 sizeof(FILE_FS_SIZE_INFORMATION),
633 FileFsSizeInformation);
634 if (!NT_SUCCESS(Status))
635 {
636 ZwClose(FileHandle);
637 return Status;
638 }
639
640 BytesPerAllocationUnit = FsSizeInformation.SectorsPerAllocationUnit *
641 FsSizeInformation.BytesPerSector;
642 /* FIXME: If we have 2048 BytesPerAllocationUnit (FAT16 < 128MB) there is
643 * a problem if the paging file is fragmented. Suppose the first cluster
644 * of the paging file is cluster 3042 but cluster 3043 is NOT part of the
645 * paging file but of another file. We can't write a complete page (4096
646 * bytes) to the physical location of cluster 3042 then. */
647 if (BytesPerAllocationUnit % PAGE_SIZE)
648 {
649 DPRINT1("BytesPerAllocationUnit %d is not a multiple of PAGE_SIZE %d\n",
650 BytesPerAllocationUnit, PAGE_SIZE);
651 ZwClose(FileHandle);
652 return STATUS_UNSUCCESSFUL;
653 }
654
655 Status = ZwSetInformationFile(FileHandle,
656 &IoStatus,
657 &SafeInitialSize,
658 sizeof(LARGE_INTEGER),
659 FileAllocationInformation);
660 if (!NT_SUCCESS(Status))
661 {
662 ZwClose(FileHandle);
663 return(Status);
664 }
665
666 Status = ObReferenceObjectByHandle(FileHandle,
667 FILE_ALL_ACCESS,
668 IoFileObjectType,
669 PreviousMode,
670 (PVOID*)&FileObject,
671 NULL);
672 if (!NT_SUCCESS(Status))
673 {
674 ZwClose(FileHandle);
675 return(Status);
676 }
677
678 CurrentRetDescList = RetDescList = MmAllocRetrievelDescriptorList(PAIRS_PER_RUN);
679
680 if (CurrentRetDescList == NULL)
681 {
682 ObDereferenceObject(FileObject);
683 ZwClose(FileHandle);
684 return(STATUS_NO_MEMORY);
685 }
686
687 #if defined(__GNUC__)
688 Vcn.QuadPart = 0LL;
689 #else
690
691 Vcn.QuadPart = 0;
692 #endif
693
694 ExtentCount = 0;
695 MaxVcn.QuadPart = (SafeInitialSize.QuadPart + BytesPerAllocationUnit - 1) / BytesPerAllocationUnit;
696 while(1)
697 {
698 Status = ZwFsControlFile(FileHandle,
699 0,
700 NULL,
701 NULL,
702 &IoStatus,
703 FSCTL_GET_RETRIEVAL_POINTERS,
704 &Vcn,
705 sizeof(LARGE_INTEGER),
706 &CurrentRetDescList->RetrievalPointers,
707 sizeof(RETRIEVAL_POINTERS_BUFFER) + PAIRS_PER_RUN * 2 * sizeof(LARGE_INTEGER));
708 if (!NT_SUCCESS(Status))
709 {
710 while (RetDescList)
711 {
712 CurrentRetDescList = RetDescList;
713 RetDescList = RetDescList->Next;
714 ExFreePool(CurrentRetDescList);
715 }
716 ObDereferenceObject(FileObject);
717 ZwClose(FileHandle);
718 return(Status);
719 }
720 ExtentCount += CurrentRetDescList->RetrievalPointers.ExtentCount;
721 if (CurrentRetDescList->RetrievalPointers.Extents[CurrentRetDescList->RetrievalPointers.ExtentCount-1].NextVcn.QuadPart < MaxVcn.QuadPart)
722 {
723 CurrentRetDescList->Next = MmAllocRetrievelDescriptorList(PAIRS_PER_RUN);
724 if (CurrentRetDescList->Next == NULL)
725 {
726 while (RetDescList)
727 {
728 CurrentRetDescList = RetDescList;
729 RetDescList = RetDescList->Next;
730 ExFreePool(CurrentRetDescList);
731 }
732 ObDereferenceObject(FileObject);
733 ZwClose(FileHandle);
734 return(STATUS_NO_MEMORY);
735 }
736 Vcn = CurrentRetDescList->RetrievalPointers.Extents[CurrentRetDescList->RetrievalPointers.ExtentCount-1].NextVcn;
737 CurrentRetDescList = CurrentRetDescList->Next;
738 }
739 else
740 {
741 break;
742 }
743 }
744
745 PagingFile = ExAllocatePool(NonPagedPool, sizeof(*PagingFile));
746 if (PagingFile == NULL)
747 {
748 while (RetDescList)
749 {
750 CurrentRetDescList = RetDescList;
751 RetDescList = RetDescList->Next;
752 ExFreePool(CurrentRetDescList);
753 }
754 ObDereferenceObject(FileObject);
755 ZwClose(FileHandle);
756 return(STATUS_NO_MEMORY);
757 }
758
759 RtlZeroMemory(PagingFile, sizeof(*PagingFile));
760
761 PagingFile->FileObject = FileObject;
762 PagingFile->MaximumSize.QuadPart = SafeMaximumSize.QuadPart;
763 PagingFile->CurrentSize.QuadPart = SafeInitialSize.QuadPart;
764 PagingFile->FreePages = (ULONG)(SafeInitialSize.QuadPart / PAGE_SIZE);
765 PagingFile->UsedPages = 0;
766 KeInitializeSpinLock(&PagingFile->AllocMapLock);
767
768 AllocMapSize = (PagingFile->FreePages / 32) + 1;
769 PagingFile->AllocMap = ExAllocatePool(NonPagedPool,
770 AllocMapSize * sizeof(ULONG));
771 PagingFile->AllocMapSize = AllocMapSize;
772
773 if (PagingFile->AllocMap == NULL)
774 {
775 while (RetDescList)
776 {
777 CurrentRetDescList = RetDescList;
778 RetDescList = RetDescList->Next;
779 ExFreePool(CurrentRetDescList);
780 }
781 ExFreePool(PagingFile);
782 ObDereferenceObject(FileObject);
783 ZwClose(FileHandle);
784 return(STATUS_NO_MEMORY);
785 }
786 DPRINT("ExtentCount: %d\n", ExtentCount);
787 Size = sizeof(RETRIEVAL_POINTERS_BUFFER) + ExtentCount * 2 * sizeof(LARGE_INTEGER);
788 PagingFile->RetrievalPointers = ExAllocatePool(NonPagedPool, Size);
789 if (PagingFile->RetrievalPointers == NULL)
790 {
791 while (RetDescList)
792 {
793 CurrentRetDescList = RetDescList;
794 RetDescList = RetDescList->Next;
795 ExFreePool(CurrentRetDescList);
796 }
797 ExFreePool(PagingFile->AllocMap);
798 ExFreePool(PagingFile);
799 ObDereferenceObject(FileObject);
800 ZwClose(FileHandle);
801 return(STATUS_NO_MEMORY);
802 }
803
804 RtlZeroMemory(PagingFile->AllocMap, AllocMapSize * sizeof(ULONG));
805 RtlZeroMemory(PagingFile->RetrievalPointers, Size);
806
807 Count = 0;
808 PagingFile->RetrievalPointers->ExtentCount = ExtentCount;
809 PagingFile->RetrievalPointers->StartingVcn = RetDescList->RetrievalPointers.StartingVcn;
810 CurrentRetDescList = RetDescList;
811 while (CurrentRetDescList)
812 {
813 memcpy(&PagingFile->RetrievalPointers->Extents[Count],
814 CurrentRetDescList->RetrievalPointers.Extents,
815 CurrentRetDescList->RetrievalPointers.ExtentCount * 2 * sizeof(LARGE_INTEGER));
816 Count += CurrentRetDescList->RetrievalPointers.ExtentCount;
817 RetDescList = CurrentRetDescList;
818 CurrentRetDescList = CurrentRetDescList->Next;
819 ExFreePool(RetDescList);
820 }
821
822 if (PagingFile->RetrievalPointers->ExtentCount != ExtentCount ||
823 PagingFile->RetrievalPointers->Extents[ExtentCount - 1].NextVcn.QuadPart != MaxVcn.QuadPart)
824 {
825 ExFreePool(PagingFile->RetrievalPointers);
826 ExFreePool(PagingFile->AllocMap);
827 ExFreePool(PagingFile);
828 ObDereferenceObject(FileObject);
829 ZwClose(FileHandle);
830 return(STATUS_UNSUCCESSFUL);
831 }
832
833 /*
834 * Change the entries from lcn's to volume offset's.
835 */
836 PagingFile->RetrievalPointers->StartingVcn.QuadPart *= BytesPerAllocationUnit;
837 for (i = 0; i < ExtentCount; i++)
838 {
839 PagingFile->RetrievalPointers->Extents[i].Lcn.QuadPart *= BytesPerAllocationUnit;
840 PagingFile->RetrievalPointers->Extents[i].NextVcn.QuadPart *= BytesPerAllocationUnit;
841 }
842
843 KeAcquireSpinLock(&PagingFileListLock, &oldIrql);
844 for (i = 0; i < MAX_PAGING_FILES; i++)
845 {
846 if (PagingFileList[i] == NULL)
847 {
848 PagingFileList[i] = PagingFile;
849 break;
850 }
851 }
852 MiFreeSwapPages = MiFreeSwapPages + PagingFile->FreePages;
853 MiPagingFileCount++;
854 KeReleaseSpinLock(&PagingFileListLock, oldIrql);
855
856 ZwClose(FileHandle);
857
858 MmSwapSpaceMessage = FALSE;
859
860 return(STATUS_SUCCESS);
861 }
862
863 /* EOF */