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