Sync with trunk (r47116), hopefully without breaking anything.
[reactos.git] / ntoskrnl / mm / pagefile.c
1 /*
2 * ReactOS kernel
3 * Copyright (C) 1998, 1999, 2000, 2001 ReactOS Team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19 /*
20 * PROJECT: ReactOS kernel
21 * FILE: ntoskrnl/mm/pagefile.c
22 * PURPOSE: Paging file functions
23 * PROGRAMMER: David Welch (welch@mcmail.com)
24 * UPDATE HISTORY:
25 * Created 22/05/98
26 */
27
28 /* INCLUDES *****************************************************************/
29
30 #include <ntoskrnl.h>
31 #define NDEBUG
32 #include <debug.h>
33
34 #if defined (ALLOC_PRAGMA)
35 #pragma alloc_text(INIT, MmInitPagingFile)
36 #endif
37
38 PVOID
39 NTAPI
40 MiFindExportedRoutineByName(IN PVOID DllBase,
41 IN PANSI_STRING ExportName);
42
43 /* TYPES *********************************************************************/
44
45 typedef struct _PAGINGFILE
46 {
47 LIST_ENTRY PagingFileListEntry;
48 PFILE_OBJECT FileObject;
49 LARGE_INTEGER MaximumSize;
50 LARGE_INTEGER CurrentSize;
51 ULONG FreePages;
52 ULONG UsedPages;
53 PULONG AllocMap;
54 KSPIN_LOCK AllocMapLock;
55 ULONG AllocMapSize;
56 PRETRIEVAL_POINTERS_BUFFER RetrievalPointers;
57 }
58 PAGINGFILE, *PPAGINGFILE;
59
60 typedef struct _RETRIEVEL_DESCRIPTOR_LIST
61 {
62 struct _RETRIEVEL_DESCRIPTOR_LIST* Next;
63 RETRIEVAL_POINTERS_BUFFER RetrievalPointers;
64 }
65 RETRIEVEL_DESCRIPTOR_LIST, *PRETRIEVEL_DESCRIPTOR_LIST;
66
67 /* GLOBALS *******************************************************************/
68
69 #define PAIRS_PER_RUN (1024)
70
71 #define MAX_PAGING_FILES (32)
72
73 /* List of paging files, both used and free */
74 static PPAGINGFILE PagingFileList[MAX_PAGING_FILES];
75
76 /* Lock for examining the list of paging files */
77 static KSPIN_LOCK PagingFileListLock;
78
79 /* Number of paging files */
80 static ULONG MiPagingFileCount;
81 ULONG MmNumberOfPagingFiles;
82
83 /* Number of pages that are available for swapping */
84 ULONG MiFreeSwapPages;
85
86 /* Number of pages that have been allocated for swapping */
87 ULONG MiUsedSwapPages;
88
89 BOOLEAN MmZeroPageFile;
90
91 /*
92 * Number of pages that have been reserved for swapping but not yet allocated
93 */
94 static ULONG 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_TYPE Pages)
125 {
126 memcpy(Mdl + 1, Pages, sizeof(PFN_TYPE) * (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 MmIsFileAPagingFile(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_TYPE Page)
221 {
222 ULONG i, offset;
223 LARGE_INTEGER file_offset;
224 IO_STATUS_BLOCK Iosb;
225 NTSTATUS Status;
226 KEVENT Event;
227 UCHAR MdlBase[sizeof(MDL) + sizeof(ULONG)];
228 PMDL Mdl = (PMDL)MdlBase;
229
230 DPRINT("MmWriteToSwapPage\n");
231
232 if (SwapEntry == 0)
233 {
234 KeBugCheck(MEMORY_MANAGEMENT);
235 return(STATUS_UNSUCCESSFUL);
236 }
237
238 i = FILE_FROM_ENTRY(SwapEntry);
239 offset = OFFSET_FROM_ENTRY(SwapEntry);
240
241 if (i >= MAX_PAGING_FILES)
242 {
243 DPRINT1("Bad swap entry 0x%.8X\n", SwapEntry);
244 KeBugCheck(MEMORY_MANAGEMENT);
245 }
246 if (PagingFileList[i]->FileObject == NULL ||
247 PagingFileList[i]->FileObject->DeviceObject == NULL)
248 {
249 DPRINT1("Bad paging file 0x%.8X\n", SwapEntry);
250 KeBugCheck(MEMORY_MANAGEMENT);
251 }
252
253 MmInitializeMdl(Mdl, NULL, PAGE_SIZE);
254 MmBuildMdlFromPages(Mdl, &Page);
255 Mdl->MdlFlags |= MDL_PAGES_LOCKED;
256
257 file_offset.QuadPart = offset * PAGE_SIZE;
258 file_offset = MmGetOffsetPageFile(PagingFileList[i]->RetrievalPointers, file_offset);
259
260 KeInitializeEvent(&Event, NotificationEvent, FALSE);
261 Status = IoSynchronousPageWrite(PagingFileList[i]->FileObject,
262 Mdl,
263 &file_offset,
264 &Event,
265 &Iosb);
266 if (Status == STATUS_PENDING)
267 {
268 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
269 Status = Iosb.Status;
270 }
271
272 if (Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA)
273 {
274 MmUnmapLockedPages (Mdl->MappedSystemVa, Mdl);
275 }
276 return(Status);
277 }
278
279 NTSTATUS
280 NTAPI
281 MmReadFromSwapPage(SWAPENTRY SwapEntry, PFN_TYPE Page)
282 {
283 ULONG i, offset;
284 LARGE_INTEGER file_offset;
285 IO_STATUS_BLOCK Iosb;
286 NTSTATUS Status;
287 KEVENT Event;
288 UCHAR MdlBase[sizeof(MDL) + sizeof(ULONG)];
289 PMDL Mdl = (PMDL)MdlBase;
290
291 DPRINT("MmReadFromSwapPage\n");
292
293 if (SwapEntry == 0)
294 {
295 KeBugCheck(MEMORY_MANAGEMENT);
296 return(STATUS_UNSUCCESSFUL);
297 }
298
299 i = FILE_FROM_ENTRY(SwapEntry);
300 offset = OFFSET_FROM_ENTRY(SwapEntry);
301
302 if (i >= MAX_PAGING_FILES)
303 {
304 DPRINT1("Bad swap entry 0x%.8X\n", SwapEntry);
305 KeBugCheck(MEMORY_MANAGEMENT);
306 }
307 if (PagingFileList[i]->FileObject == NULL ||
308 PagingFileList[i]->FileObject->DeviceObject == NULL)
309 {
310 DPRINT1("Bad paging file 0x%.8X\n", SwapEntry);
311 KeBugCheck(MEMORY_MANAGEMENT);
312 }
313
314 MmInitializeMdl(Mdl, NULL, PAGE_SIZE);
315 MmBuildMdlFromPages(Mdl, &Page);
316 Mdl->MdlFlags |= MDL_PAGES_LOCKED;
317
318 file_offset.QuadPart = offset * PAGE_SIZE;
319 file_offset = MmGetOffsetPageFile(PagingFileList[i]->RetrievalPointers, file_offset);
320
321 KeInitializeEvent(&Event, NotificationEvent, FALSE);
322 Status = IoPageRead(PagingFileList[i]->FileObject,
323 Mdl,
324 &file_offset,
325 &Event,
326 &Iosb);
327 if (Status == STATUS_PENDING)
328 {
329 KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
330 Status = Iosb.Status;
331 }
332 if (Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA)
333 {
334 MmUnmapLockedPages (Mdl->MappedSystemVa, Mdl);
335 }
336 return(Status);
337 }
338
339 VOID
340 INIT_FUNCTION
341 NTAPI
342 MmInitPagingFile(VOID)
343 {
344 ULONG i;
345
346 KeInitializeSpinLock(&PagingFileListLock);
347
348 MiFreeSwapPages = 0;
349 MiUsedSwapPages = 0;
350 MiReservedSwapPages = 0;
351
352 for (i = 0; i < MAX_PAGING_FILES; i++)
353 {
354 PagingFileList[i] = NULL;
355 }
356 MiPagingFileCount = 0;
357 }
358
359 BOOLEAN
360 NTAPI
361 MmReserveSwapPages(ULONG Nr)
362 {
363 KIRQL oldIrql;
364 ULONG MiAvailSwapPages;
365
366 KeAcquireSpinLock(&PagingFileListLock, &oldIrql);
367 MiAvailSwapPages =
368 (MiFreeSwapPages * MM_PAGEFILE_COMMIT_RATIO) + MM_PAGEFILE_COMMIT_GRACE;
369 MiReservedSwapPages = MiReservedSwapPages + Nr;
370 if ((MM_PAGEFILE_COMMIT_RATIO != 0) && (MiAvailSwapPages < MiReservedSwapPages))
371 {
372 KeReleaseSpinLock(&PagingFileListLock, oldIrql);
373 return(FALSE);
374 }
375 KeReleaseSpinLock(&PagingFileListLock, oldIrql);
376 return(TRUE);
377 }
378
379 VOID
380 NTAPI
381 MmDereserveSwapPages(ULONG Nr)
382 {
383 KIRQL oldIrql;
384
385 KeAcquireSpinLock(&PagingFileListLock, &oldIrql);
386 MiReservedSwapPages = MiReservedSwapPages - Nr;
387 KeReleaseSpinLock(&PagingFileListLock, oldIrql);
388 }
389
390 static ULONG
391 MiAllocPageFromPagingFile(PPAGINGFILE PagingFile)
392 {
393 KIRQL oldIrql;
394 ULONG i, j;
395
396 KeAcquireSpinLock(&PagingFile->AllocMapLock, &oldIrql);
397
398 for (i = 0; i < PagingFile->AllocMapSize; i++)
399 {
400 for (j = 0; j < 32; j++)
401 {
402 if (!(PagingFile->AllocMap[i] & (1 << j)))
403 {
404 PagingFile->AllocMap[i] |= (1 << j);
405 PagingFile->UsedPages++;
406 PagingFile->FreePages--;
407 KeReleaseSpinLock(&PagingFile->AllocMapLock, oldIrql);
408 return((i * 32) + j);
409 }
410 }
411 }
412
413 KeReleaseSpinLock(&PagingFile->AllocMapLock, oldIrql);
414 return(0xFFFFFFFF);
415 }
416
417 VOID
418 NTAPI
419 MmFreeSwapPage(SWAPENTRY Entry)
420 {
421 ULONG i;
422 ULONG off;
423 KIRQL oldIrql;
424
425 i = FILE_FROM_ENTRY(Entry);
426 off = OFFSET_FROM_ENTRY(Entry);
427
428 if (i >= MAX_PAGING_FILES)
429 {
430 DPRINT1("Bad swap entry 0x%.8X\n", Entry);
431 KeBugCheck(MEMORY_MANAGEMENT);
432 }
433
434 KeAcquireSpinLock(&PagingFileListLock, &oldIrql);
435 if (PagingFileList[i] == NULL)
436 {
437 KeBugCheck(MEMORY_MANAGEMENT);
438 }
439 KeAcquireSpinLockAtDpcLevel(&PagingFileList[i]->AllocMapLock);
440
441 PagingFileList[i]->AllocMap[off >> 5] &= (~(1 << (off % 32)));
442
443 PagingFileList[i]->FreePages++;
444 PagingFileList[i]->UsedPages--;
445
446 MiFreeSwapPages++;
447 MiUsedSwapPages--;
448
449 KeReleaseSpinLockFromDpcLevel(&PagingFileList[i]->AllocMapLock);
450 KeReleaseSpinLock(&PagingFileListLock, oldIrql);
451 }
452
453 BOOLEAN
454 NTAPI
455 MmIsAvailableSwapPage(VOID)
456 {
457 return(MiFreeSwapPages > 0);
458 }
459
460 SWAPENTRY
461 NTAPI
462 MmAllocSwapPage(VOID)
463 {
464 KIRQL oldIrql;
465 ULONG i;
466 ULONG off;
467 SWAPENTRY entry;
468
469 KeAcquireSpinLock(&PagingFileListLock, &oldIrql);
470
471 if (MiFreeSwapPages == 0)
472 {
473 KeReleaseSpinLock(&PagingFileListLock, oldIrql);
474 return(0);
475 }
476
477 for (i = 0; i < MAX_PAGING_FILES; i++)
478 {
479 if (PagingFileList[i] != NULL &&
480 PagingFileList[i]->FreePages >= 1)
481 {
482 off = MiAllocPageFromPagingFile(PagingFileList[i]);
483 if (off == 0xFFFFFFFF)
484 {
485 KeBugCheck(MEMORY_MANAGEMENT);
486 KeReleaseSpinLock(&PagingFileListLock, oldIrql);
487 return(STATUS_UNSUCCESSFUL);
488 }
489 MiUsedSwapPages++;
490 MiFreeSwapPages--;
491 KeReleaseSpinLock(&PagingFileListLock, oldIrql);
492
493 entry = ENTRY_FROM_FILE_OFFSET(i, off);
494 return(entry);
495 }
496 }
497
498 KeReleaseSpinLock(&PagingFileListLock, oldIrql);
499 KeBugCheck(MEMORY_MANAGEMENT);
500 return(0);
501 }
502
503 static PRETRIEVEL_DESCRIPTOR_LIST FASTCALL
504 MmAllocRetrievelDescriptorList(ULONG Pairs)
505 {
506 ULONG Size;
507 PRETRIEVEL_DESCRIPTOR_LIST RetDescList;
508
509 Size = sizeof(RETRIEVEL_DESCRIPTOR_LIST) + Pairs * 2 * sizeof(LARGE_INTEGER);
510 RetDescList = ExAllocatePool(NonPagedPool, Size);
511 if (RetDescList)
512 {
513 RtlZeroMemory(RetDescList, Size);
514 }
515
516 return RetDescList;
517 }
518
519 NTSTATUS NTAPI
520 NtCreatePagingFile(IN PUNICODE_STRING FileName,
521 IN PLARGE_INTEGER InitialSize,
522 IN PLARGE_INTEGER MaximumSize,
523 IN ULONG Reserved)
524 {
525 NTSTATUS Status;
526 OBJECT_ATTRIBUTES ObjectAttributes;
527 HANDLE FileHandle;
528 IO_STATUS_BLOCK IoStatus;
529 PFILE_OBJECT FileObject;
530 PPAGINGFILE PagingFile;
531 KIRQL oldIrql;
532 ULONG AllocMapSize;
533 FILE_FS_SIZE_INFORMATION FsSizeInformation;
534 PRETRIEVEL_DESCRIPTOR_LIST RetDescList;
535 PRETRIEVEL_DESCRIPTOR_LIST CurrentRetDescList;
536 ULONG i;
537 ULONG BytesPerAllocationUnit;
538 LARGE_INTEGER Vcn;
539 ULONG ExtentCount;
540 LARGE_INTEGER MaxVcn;
541 ULONG Count;
542 ULONG Size;
543 KPROCESSOR_MODE PreviousMode;
544 UNICODE_STRING CapturedFileName;
545 LARGE_INTEGER SafeInitialSize, SafeMaximumSize;
546
547 DPRINT("NtCreatePagingFile(FileName %wZ, InitialSize %I64d)\n",
548 FileName, InitialSize->QuadPart);
549
550 if (MiPagingFileCount >= MAX_PAGING_FILES)
551 {
552 return(STATUS_TOO_MANY_PAGING_FILES);
553 }
554
555 PreviousMode = ExGetPreviousMode();
556
557 if (PreviousMode != KernelMode)
558 {
559 _SEH2_TRY
560 {
561 SafeInitialSize = ProbeForReadLargeInteger(InitialSize);
562 SafeMaximumSize = ProbeForReadLargeInteger(MaximumSize);
563 }
564 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
565 {
566 /* Return the exception code */
567 _SEH2_YIELD(return _SEH2_GetExceptionCode());
568 }
569 _SEH2_END;
570 }
571 else
572 {
573 SafeInitialSize = *InitialSize;
574 SafeMaximumSize = *MaximumSize;
575 }
576
577 /* Pagefiles can't be larger than 4GB and ofcourse the minimum should be
578 smaller than the maximum */
579 if (0 != SafeInitialSize.u.HighPart)
580 {
581 return STATUS_INVALID_PARAMETER_2;
582 }
583 if (0 != SafeMaximumSize.u.HighPart)
584 {
585 return STATUS_INVALID_PARAMETER_3;
586 }
587 if (SafeMaximumSize.u.LowPart < SafeInitialSize.u.LowPart)
588 {
589 return STATUS_INVALID_PARAMETER_MIX;
590 }
591
592 Status = ProbeAndCaptureUnicodeString(&CapturedFileName,
593 PreviousMode,
594 FileName);
595 if (!NT_SUCCESS(Status))
596 {
597 return(Status);
598 }
599
600 InitializeObjectAttributes(&ObjectAttributes,
601 &CapturedFileName,
602 0,
603 NULL,
604 NULL);
605
606 Status = IoCreateFile(&FileHandle,
607 FILE_ALL_ACCESS,
608 &ObjectAttributes,
609 &IoStatus,
610 NULL,
611 0,
612 0,
613 FILE_OPEN_IF,
614 FILE_SYNCHRONOUS_IO_NONALERT,
615 NULL,
616 0,
617 CreateFileTypeNone,
618 NULL,
619 SL_OPEN_PAGING_FILE | IO_NO_PARAMETER_CHECKING);
620
621 ReleaseCapturedUnicodeString(&CapturedFileName,
622 PreviousMode);
623 if (!NT_SUCCESS(Status))
624 {
625 return(Status);
626 }
627
628 Status = ZwQueryVolumeInformationFile(FileHandle,
629 &IoStatus,
630 &FsSizeInformation,
631 sizeof(FILE_FS_SIZE_INFORMATION),
632 FileFsSizeInformation);
633 if (!NT_SUCCESS(Status))
634 {
635 ZwClose(FileHandle);
636 return Status;
637 }
638
639 BytesPerAllocationUnit = FsSizeInformation.SectorsPerAllocationUnit *
640 FsSizeInformation.BytesPerSector;
641 /* FIXME: If we have 2048 BytesPerAllocationUnit (FAT16 < 128MB) there is
642 * a problem if the paging file is fragmented. Suppose the first cluster
643 * of the paging file is cluster 3042 but cluster 3043 is NOT part of the
644 * paging file but of another file. We can't write a complete page (4096
645 * bytes) to the physical location of cluster 3042 then. */
646 if (BytesPerAllocationUnit % PAGE_SIZE)
647 {
648 DPRINT1("BytesPerAllocationUnit %d is not a multiple of PAGE_SIZE %d\n",
649 BytesPerAllocationUnit, PAGE_SIZE);
650 ZwClose(FileHandle);
651 return STATUS_UNSUCCESSFUL;
652 }
653
654 Status = ZwSetInformationFile(FileHandle,
655 &IoStatus,
656 &SafeInitialSize,
657 sizeof(LARGE_INTEGER),
658 FileAllocationInformation);
659 if (!NT_SUCCESS(Status))
660 {
661 ZwClose(FileHandle);
662 return(Status);
663 }
664
665 Status = ObReferenceObjectByHandle(FileHandle,
666 FILE_ALL_ACCESS,
667 IoFileObjectType,
668 PreviousMode,
669 (PVOID*)&FileObject,
670 NULL);
671 if (!NT_SUCCESS(Status))
672 {
673 ZwClose(FileHandle);
674 return(Status);
675 }
676
677 CurrentRetDescList = RetDescList = MmAllocRetrievelDescriptorList(PAIRS_PER_RUN);
678
679 if (CurrentRetDescList == NULL)
680 {
681 ObDereferenceObject(FileObject);
682 ZwClose(FileHandle);
683 return(STATUS_NO_MEMORY);
684 }
685
686 #if defined(__GNUC__)
687 Vcn.QuadPart = 0LL;
688 #else
689
690 Vcn.QuadPart = 0;
691 #endif
692
693 ExtentCount = 0;
694 MaxVcn.QuadPart = (SafeInitialSize.QuadPart + BytesPerAllocationUnit - 1) / BytesPerAllocationUnit;
695 while(1)
696 {
697 Status = ZwFsControlFile(FileHandle,
698 0,
699 NULL,
700 NULL,
701 &IoStatus,
702 FSCTL_GET_RETRIEVAL_POINTERS,
703 &Vcn,
704 sizeof(LARGE_INTEGER),
705 &CurrentRetDescList->RetrievalPointers,
706 sizeof(RETRIEVAL_POINTERS_BUFFER) + PAIRS_PER_RUN * 2 * sizeof(LARGE_INTEGER));
707 if (!NT_SUCCESS(Status))
708 {
709 while (RetDescList)
710 {
711 CurrentRetDescList = RetDescList;
712 RetDescList = RetDescList->Next;
713 ExFreePool(CurrentRetDescList);
714 }
715 ObDereferenceObject(FileObject);
716 ZwClose(FileHandle);
717 return(Status);
718 }
719 ExtentCount += CurrentRetDescList->RetrievalPointers.ExtentCount;
720 if (CurrentRetDescList->RetrievalPointers.Extents[CurrentRetDescList->RetrievalPointers.ExtentCount-1].NextVcn.QuadPart < MaxVcn.QuadPart)
721 {
722 CurrentRetDescList->Next = MmAllocRetrievelDescriptorList(PAIRS_PER_RUN);
723 if (CurrentRetDescList->Next == NULL)
724 {
725 while (RetDescList)
726 {
727 CurrentRetDescList = RetDescList;
728 RetDescList = RetDescList->Next;
729 ExFreePool(CurrentRetDescList);
730 }
731 ObDereferenceObject(FileObject);
732 ZwClose(FileHandle);
733 return(STATUS_NO_MEMORY);
734 }
735 Vcn = CurrentRetDescList->RetrievalPointers.Extents[CurrentRetDescList->RetrievalPointers.ExtentCount-1].NextVcn;
736 CurrentRetDescList = CurrentRetDescList->Next;
737 }
738 else
739 {
740 break;
741 }
742 }
743
744 PagingFile = ExAllocatePool(NonPagedPool, sizeof(*PagingFile));
745 if (PagingFile == NULL)
746 {
747 while (RetDescList)
748 {
749 CurrentRetDescList = RetDescList;
750 RetDescList = RetDescList->Next;
751 ExFreePool(CurrentRetDescList);
752 }
753 ObDereferenceObject(FileObject);
754 ZwClose(FileHandle);
755 return(STATUS_NO_MEMORY);
756 }
757
758 RtlZeroMemory(PagingFile, sizeof(*PagingFile));
759
760 PagingFile->FileObject = FileObject;
761 PagingFile->MaximumSize.QuadPart = SafeMaximumSize.QuadPart;
762 PagingFile->CurrentSize.QuadPart = SafeInitialSize.QuadPart;
763 PagingFile->FreePages = (ULONG)(SafeInitialSize.QuadPart / PAGE_SIZE);
764 PagingFile->UsedPages = 0;
765 KeInitializeSpinLock(&PagingFile->AllocMapLock);
766
767 AllocMapSize = (PagingFile->FreePages / 32) + 1;
768 PagingFile->AllocMap = ExAllocatePool(NonPagedPool,
769 AllocMapSize * sizeof(ULONG));
770 PagingFile->AllocMapSize = AllocMapSize;
771
772 if (PagingFile->AllocMap == NULL)
773 {
774 while (RetDescList)
775 {
776 CurrentRetDescList = RetDescList;
777 RetDescList = RetDescList->Next;
778 ExFreePool(CurrentRetDescList);
779 }
780 ExFreePool(PagingFile);
781 ObDereferenceObject(FileObject);
782 ZwClose(FileHandle);
783 return(STATUS_NO_MEMORY);
784 }
785 DPRINT("ExtentCount: %d\n", ExtentCount);
786 Size = sizeof(RETRIEVAL_POINTERS_BUFFER) + ExtentCount * 2 * sizeof(LARGE_INTEGER);
787 PagingFile->RetrievalPointers = ExAllocatePool(NonPagedPool, Size);
788 if (PagingFile->RetrievalPointers == NULL)
789 {
790 while (RetDescList)
791 {
792 CurrentRetDescList = RetDescList;
793 RetDescList = RetDescList->Next;
794 ExFreePool(CurrentRetDescList);
795 }
796 ExFreePool(PagingFile->AllocMap);
797 ExFreePool(PagingFile);
798 ObDereferenceObject(FileObject);
799 ZwClose(FileHandle);
800 return(STATUS_NO_MEMORY);
801 }
802
803 RtlZeroMemory(PagingFile->AllocMap, AllocMapSize * sizeof(ULONG));
804 RtlZeroMemory(PagingFile->RetrievalPointers, Size);
805
806 Count = 0;
807 PagingFile->RetrievalPointers->ExtentCount = ExtentCount;
808 PagingFile->RetrievalPointers->StartingVcn = RetDescList->RetrievalPointers.StartingVcn;
809 CurrentRetDescList = RetDescList;
810 while (CurrentRetDescList)
811 {
812 memcpy(&PagingFile->RetrievalPointers->Extents[Count],
813 CurrentRetDescList->RetrievalPointers.Extents,
814 CurrentRetDescList->RetrievalPointers.ExtentCount * 2 * sizeof(LARGE_INTEGER));
815 Count += CurrentRetDescList->RetrievalPointers.ExtentCount;
816 RetDescList = CurrentRetDescList;
817 CurrentRetDescList = CurrentRetDescList->Next;
818 ExFreePool(RetDescList);
819 }
820
821 if (PagingFile->RetrievalPointers->ExtentCount != ExtentCount ||
822 PagingFile->RetrievalPointers->Extents[ExtentCount - 1].NextVcn.QuadPart != MaxVcn.QuadPart)
823 {
824 ExFreePool(PagingFile->RetrievalPointers);
825 ExFreePool(PagingFile->AllocMap);
826 ExFreePool(PagingFile);
827 ObDereferenceObject(FileObject);
828 ZwClose(FileHandle);
829 return(STATUS_UNSUCCESSFUL);
830 }
831
832 /*
833 * Change the entries from lcn's to volume offset's.
834 */
835 PagingFile->RetrievalPointers->StartingVcn.QuadPart *= BytesPerAllocationUnit;
836 for (i = 0; i < ExtentCount; i++)
837 {
838 PagingFile->RetrievalPointers->Extents[i].Lcn.QuadPart *= BytesPerAllocationUnit;
839 PagingFile->RetrievalPointers->Extents[i].NextVcn.QuadPart *= BytesPerAllocationUnit;
840 }
841
842 KeAcquireSpinLock(&PagingFileListLock, &oldIrql);
843 for (i = 0; i < MAX_PAGING_FILES; i++)
844 {
845 if (PagingFileList[i] == NULL)
846 {
847 PagingFileList[i] = PagingFile;
848 break;
849 }
850 }
851 MiFreeSwapPages = MiFreeSwapPages + PagingFile->FreePages;
852 MiPagingFileCount++;
853 KeReleaseSpinLock(&PagingFileListLock, oldIrql);
854
855 ZwClose(FileHandle);
856
857 MmSwapSpaceMessage = FALSE;
858
859 return(STATUS_SUCCESS);
860 }
861
862 /* EOF */