/*
 *  ReactOS kernel
 *  Copyright (C) 1998, 1999, 2000, 2001 ReactOS Team
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
/*
 * PROJECT:         ReactOS kernel
 * FILE:            ntoskrnl/mm/pagefile.c
 * PURPOSE:         Paging file functions
 * PROGRAMMER:      David Welch (welch@mcmail.com)
 * UPDATE HISTORY:
 *                  Created 22/05/98
 */

/* INCLUDES *****************************************************************/

#include <ntoskrnl.h>
#define NDEBUG
#include <debug.h>

#if defined (ALLOC_PRAGMA)
#pragma alloc_text(INIT, MmInitPagingFile)
#endif

PVOID
NTAPI
MiFindExportedRoutineByName(IN PVOID DllBase,
                            IN PANSI_STRING ExportName);

/* TYPES *********************************************************************/

typedef struct _PAGINGFILE
{
    LIST_ENTRY PagingFileListEntry;
    PFILE_OBJECT FileObject;
    LARGE_INTEGER MaximumSize;
    LARGE_INTEGER CurrentSize;
    PFN_NUMBER FreePages;
    PFN_NUMBER UsedPages;
    PULONG AllocMap;
    KSPIN_LOCK AllocMapLock;
    ULONG AllocMapSize;
    PRETRIEVAL_POINTERS_BUFFER RetrievalPointers;
}
PAGINGFILE, *PPAGINGFILE;

typedef struct _RETRIEVEL_DESCRIPTOR_LIST
{
    struct _RETRIEVEL_DESCRIPTOR_LIST* Next;
    RETRIEVAL_POINTERS_BUFFER RetrievalPointers;
}
RETRIEVEL_DESCRIPTOR_LIST, *PRETRIEVEL_DESCRIPTOR_LIST;

/* GLOBALS *******************************************************************/

#define PAIRS_PER_RUN (1024)

#define MAX_PAGING_FILES  (16)

/* List of paging files, both used and free */
static PPAGINGFILE PagingFileList[MAX_PAGING_FILES];

/* Lock for examining the list of paging files */
static KSPIN_LOCK PagingFileListLock;

/* Number of paging files */
ULONG MmNumberOfPagingFiles;

/* Number of pages that are available for swapping */
PFN_COUNT MiFreeSwapPages;

/* Number of pages that have been allocated for swapping */
PFN_COUNT MiUsedSwapPages;

BOOLEAN MmZeroPageFile;

/*
 * Number of pages that have been reserved for swapping but not yet allocated
 */
static PFN_COUNT MiReservedSwapPages;

/*
 * Ratio between reserved and available swap pages, e.g. setting this to five
 * forces one swap page to be available for every five swap pages that are
 * reserved. Setting this to zero turns off commit checking altogether.
 */
#define MM_PAGEFILE_COMMIT_RATIO      (1)

/*
 * Number of pages that can be used for potentially swapable memory without
 * pagefile space being reserved. The intention is that this allows smss
 * to start up and create page files while ordinarily having a commit
 * ratio of one.
 */
#define MM_PAGEFILE_COMMIT_GRACE      (256)

/*
 * Translate between a swap entry and a file and offset pair.
 */
#define FILE_FROM_ENTRY(i) ((i) & 0x0f)
#define OFFSET_FROM_ENTRY(i) ((i) >> 11)
#define ENTRY_FROM_FILE_OFFSET(i, j) ((i) | ((j) << 11) | 0x400)

/* Make sure there can be only 16 paging files */
C_ASSERT(FILE_FROM_ENTRY(0xffffffff) < MAX_PAGING_FILES);

static BOOLEAN MmSwapSpaceMessage = FALSE;

/* FUNCTIONS *****************************************************************/

VOID
NTAPI
MmBuildMdlFromPages(PMDL Mdl, PPFN_NUMBER Pages)
{
    memcpy(Mdl + 1, Pages, sizeof(PFN_NUMBER) * (PAGE_ROUND_UP(Mdl->ByteOffset+Mdl->ByteCount)/PAGE_SIZE));

    /* FIXME: this flag should be set by the caller perhaps? */
    Mdl->MdlFlags |= MDL_IO_PAGE_READ;
}


BOOLEAN
NTAPI
MmIsFileObjectAPagingFile(PFILE_OBJECT FileObject)
{
    ULONG i;

    /* Loop through all the paging files */
    for (i = 0; i < MmNumberOfPagingFiles; i++)
    {
        /* Check if this is one of them */
        if (PagingFileList[i]->FileObject == FileObject) return TRUE;
    }

    /* Nothing found */
    return FALSE;
}

VOID
NTAPI
MmShowOutOfSpaceMessagePagingFile(VOID)
{
    if (!MmSwapSpaceMessage)
    {
        DPRINT1("MM: Out of swap space.\n");
        MmSwapSpaceMessage = TRUE;
    }
}

static LARGE_INTEGER
MmGetOffsetPageFile(PRETRIEVAL_POINTERS_BUFFER RetrievalPointers, LARGE_INTEGER Offset)
{
    /* Simple binary search */
    ULONG first, last, mid;
    first = 0;
    last = RetrievalPointers->ExtentCount - 1;
    while (first <= last)
    {
        mid = (last - first) / 2 + first;
        if (Offset.QuadPart < RetrievalPointers->Extents[mid].NextVcn.QuadPart)
        {
            if (mid == 0)
            {
                Offset.QuadPart += RetrievalPointers->Extents[0].Lcn.QuadPart - RetrievalPointers->StartingVcn.QuadPart;
                return Offset;
            }
            else
            {
                if (Offset.QuadPart >= RetrievalPointers->Extents[mid-1].NextVcn.QuadPart)
                {
                    Offset.QuadPart += RetrievalPointers->Extents[mid].Lcn.QuadPart - RetrievalPointers->Extents[mid-1].NextVcn.QuadPart;
                    return Offset;
                }
                last = mid - 1;
            }
        }
        else
        {
            if (mid == RetrievalPointers->ExtentCount - 1)
            {
                break;
            }
            if (Offset.QuadPart < RetrievalPointers->Extents[mid+1].NextVcn.QuadPart)
            {
                Offset.QuadPart += RetrievalPointers->Extents[mid+1].Lcn.QuadPart  - RetrievalPointers->Extents[mid].NextVcn.QuadPart;
                return Offset;
            }
            first = mid + 1;
        }
    }
    KeBugCheck(MEMORY_MANAGEMENT);
#if defined(__GNUC__)

    return (LARGE_INTEGER)0LL;
#else

    {
        const LARGE_INTEGER dummy =
        {
            0
        };
        return dummy;
    }
#endif
}

NTSTATUS
NTAPI
MmWriteToSwapPage(SWAPENTRY SwapEntry, PFN_NUMBER Page)
{
    ULONG i;
    ULONG_PTR offset;
    LARGE_INTEGER file_offset;
    IO_STATUS_BLOCK Iosb;
    NTSTATUS Status;
    KEVENT Event;
    UCHAR MdlBase[sizeof(MDL) + sizeof(ULONG)];
    PMDL Mdl = (PMDL)MdlBase;

    DPRINT("MmWriteToSwapPage\n");

    if (SwapEntry == 0)
    {
        KeBugCheck(MEMORY_MANAGEMENT);
        return(STATUS_UNSUCCESSFUL);
    }

    i = FILE_FROM_ENTRY(SwapEntry);
    offset = OFFSET_FROM_ENTRY(SwapEntry);

    if (PagingFileList[i]->FileObject == NULL ||
            PagingFileList[i]->FileObject->DeviceObject == NULL)
    {
        DPRINT1("Bad paging file 0x%.8X\n", SwapEntry);
        KeBugCheck(MEMORY_MANAGEMENT);
    }

    MmInitializeMdl(Mdl, NULL, PAGE_SIZE);
    MmBuildMdlFromPages(Mdl, &Page);
    Mdl->MdlFlags |= MDL_PAGES_LOCKED;

    file_offset.QuadPart = offset * PAGE_SIZE;
    file_offset = MmGetOffsetPageFile(PagingFileList[i]->RetrievalPointers, file_offset);

    KeInitializeEvent(&Event, NotificationEvent, FALSE);
    Status = IoSynchronousPageWrite(PagingFileList[i]->FileObject,
                                    Mdl,
                                    &file_offset,
                                    &Event,
                                    &Iosb);
    if (Status == STATUS_PENDING)
    {
        KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
        Status = Iosb.Status;
    }

    if (Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA)
    {
        MmUnmapLockedPages (Mdl->MappedSystemVa, Mdl);
    }
    return(Status);
}


NTSTATUS
NTAPI
MmReadFromSwapPage(SWAPENTRY SwapEntry, PFN_NUMBER Page)
{
    return MiReadPageFile(Page, FILE_FROM_ENTRY(SwapEntry), OFFSET_FROM_ENTRY(SwapEntry));
}

NTSTATUS
NTAPI
MiReadPageFile(
    _In_ PFN_NUMBER Page,
    _In_ ULONG PageFileIndex,
    _In_ ULONG_PTR PageFileOffset)
{
    LARGE_INTEGER file_offset;
    IO_STATUS_BLOCK Iosb;
    NTSTATUS Status;
    KEVENT Event;
    UCHAR MdlBase[sizeof(MDL) + sizeof(ULONG)];
    PMDL Mdl = (PMDL)MdlBase;
    PPAGINGFILE PagingFile;

    DPRINT("MiReadSwapFile\n");

    if (PageFileOffset == 0)
    {
        KeBugCheck(MEMORY_MANAGEMENT);
        return(STATUS_UNSUCCESSFUL);
    }

    ASSERT(PageFileIndex < MAX_PAGING_FILES);

    PagingFile = PagingFileList[PageFileIndex];

    if (PagingFile->FileObject == NULL || PagingFile->FileObject->DeviceObject == NULL)
    {
        DPRINT1("Bad paging file %u\n", PageFileIndex);
        KeBugCheck(MEMORY_MANAGEMENT);
    }

    MmInitializeMdl(Mdl, NULL, PAGE_SIZE);
    MmBuildMdlFromPages(Mdl, &Page);
    Mdl->MdlFlags |= MDL_PAGES_LOCKED;

    file_offset.QuadPart = PageFileOffset * PAGE_SIZE;
    file_offset = MmGetOffsetPageFile(PagingFile->RetrievalPointers, file_offset);

    KeInitializeEvent(&Event, NotificationEvent, FALSE);
    Status = IoPageRead(PagingFile->FileObject,
                        Mdl,
                        &file_offset,
                        &Event,
                        &Iosb);
    if (Status == STATUS_PENDING)
    {
        KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
        Status = Iosb.Status;
    }
    if (Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA)
    {
        MmUnmapLockedPages (Mdl->MappedSystemVa, Mdl);
    }
    return(Status);
}

VOID
INIT_FUNCTION
NTAPI
MmInitPagingFile(VOID)
{
    ULONG i;

    KeInitializeSpinLock(&PagingFileListLock);

    MiFreeSwapPages = 0;
    MiUsedSwapPages = 0;
    MiReservedSwapPages = 0;

    for (i = 0; i < MAX_PAGING_FILES; i++)
    {
        PagingFileList[i] = NULL;
    }
    MmNumberOfPagingFiles = 0;
}

static ULONG
MiAllocPageFromPagingFile(PPAGINGFILE PagingFile)
{
    KIRQL oldIrql;
    ULONG i, j;

    KeAcquireSpinLock(&PagingFile->AllocMapLock, &oldIrql);

    for (i = 0; i < PagingFile->AllocMapSize; i++)
    {
        for (j = 0; j < 32; j++)
        {
            if (!(PagingFile->AllocMap[i] & (1 << j)))
            {
                PagingFile->AllocMap[i] |= (1 << j);
                PagingFile->UsedPages++;
                PagingFile->FreePages--;
                KeReleaseSpinLock(&PagingFile->AllocMapLock, oldIrql);
                return((i * 32) + j);
            }
        }
    }

    KeReleaseSpinLock(&PagingFile->AllocMapLock, oldIrql);
    return(0xFFFFFFFF);
}

VOID
NTAPI
MmFreeSwapPage(SWAPENTRY Entry)
{
    ULONG i;
    ULONG_PTR off;
    KIRQL oldIrql;

    i = FILE_FROM_ENTRY(Entry);
    off = OFFSET_FROM_ENTRY(Entry);

    KeAcquireSpinLock(&PagingFileListLock, &oldIrql);
    if (PagingFileList[i] == NULL)
    {
        KeBugCheck(MEMORY_MANAGEMENT);
    }
    KeAcquireSpinLockAtDpcLevel(&PagingFileList[i]->AllocMapLock);

    PagingFileList[i]->AllocMap[off >> 5] &= (~(1 << (off % 32)));

    PagingFileList[i]->FreePages++;
    PagingFileList[i]->UsedPages--;

    MiFreeSwapPages++;
    MiUsedSwapPages--;

    KeReleaseSpinLockFromDpcLevel(&PagingFileList[i]->AllocMapLock);
    KeReleaseSpinLock(&PagingFileListLock, oldIrql);
}

SWAPENTRY
NTAPI
MmAllocSwapPage(VOID)
{
    KIRQL oldIrql;
    ULONG i;
    ULONG off;
    SWAPENTRY entry;

    KeAcquireSpinLock(&PagingFileListLock, &oldIrql);

    if (MiFreeSwapPages == 0)
    {
        KeReleaseSpinLock(&PagingFileListLock, oldIrql);
        return(0);
    }

    for (i = 0; i < MAX_PAGING_FILES; i++)
    {
        if (PagingFileList[i] != NULL &&
                PagingFileList[i]->FreePages >= 1)
        {
            off = MiAllocPageFromPagingFile(PagingFileList[i]);
            if (off == 0xFFFFFFFF)
            {
                KeBugCheck(MEMORY_MANAGEMENT);
                KeReleaseSpinLock(&PagingFileListLock, oldIrql);
                return(STATUS_UNSUCCESSFUL);
            }
            MiUsedSwapPages++;
            MiFreeSwapPages--;
            KeReleaseSpinLock(&PagingFileListLock, oldIrql);

            entry = ENTRY_FROM_FILE_OFFSET(i, off);
            return(entry);
        }
    }

    KeReleaseSpinLock(&PagingFileListLock, oldIrql);
    KeBugCheck(MEMORY_MANAGEMENT);
    return(0);
}

static PRETRIEVEL_DESCRIPTOR_LIST FASTCALL
MmAllocRetrievelDescriptorList(ULONG Pairs)
{
    ULONG Size;
    PRETRIEVEL_DESCRIPTOR_LIST RetDescList;

    Size = sizeof(RETRIEVEL_DESCRIPTOR_LIST) + Pairs * 2 * sizeof(LARGE_INTEGER);
    RetDescList = ExAllocatePool(NonPagedPool, Size);
    if (RetDescList)
    {
        RtlZeroMemory(RetDescList, Size);
    }

    return RetDescList;
}

NTSTATUS NTAPI
NtCreatePagingFile(IN PUNICODE_STRING FileName,
                   IN PLARGE_INTEGER InitialSize,
                   IN PLARGE_INTEGER MaximumSize,
                   IN ULONG Reserved)
{
    NTSTATUS Status;
    OBJECT_ATTRIBUTES ObjectAttributes;
    HANDLE FileHandle;
    IO_STATUS_BLOCK IoStatus;
    PFILE_OBJECT FileObject;
    PPAGINGFILE PagingFile;
    KIRQL oldIrql;
    ULONG AllocMapSize;
    FILE_FS_SIZE_INFORMATION FsSizeInformation;
    PRETRIEVEL_DESCRIPTOR_LIST RetDescList;
    PRETRIEVEL_DESCRIPTOR_LIST CurrentRetDescList;
    ULONG i;
    ULONG BytesPerAllocationUnit;
    LARGE_INTEGER Vcn;
    ULONG ExtentCount;
    LARGE_INTEGER MaxVcn;
    ULONG Count;
    ULONG Size;
    KPROCESSOR_MODE PreviousMode;
    UNICODE_STRING CapturedFileName;
    LARGE_INTEGER SafeInitialSize, SafeMaximumSize;

    DPRINT("NtCreatePagingFile(FileName %wZ, InitialSize %I64d)\n",
           FileName, InitialSize->QuadPart);

    if (MmNumberOfPagingFiles >= MAX_PAGING_FILES)
    {
        return(STATUS_TOO_MANY_PAGING_FILES);
    }

    PreviousMode = ExGetPreviousMode();

    if (PreviousMode != KernelMode)
    {
        _SEH2_TRY
        {
            SafeInitialSize = ProbeForReadLargeInteger(InitialSize);
            SafeMaximumSize = ProbeForReadLargeInteger(MaximumSize);
        }
        _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
        {
            /* Return the exception code */
            _SEH2_YIELD(return _SEH2_GetExceptionCode());
        }
        _SEH2_END;
    }
    else
    {
        SafeInitialSize = *InitialSize;
        SafeMaximumSize = *MaximumSize;
    }

    /* Pagefiles can't be larger than 4GB and ofcourse the minimum should be
       smaller than the maximum */
    if (0 != SafeInitialSize.u.HighPart)
    {
        return STATUS_INVALID_PARAMETER_2;
    }
    if (0 != SafeMaximumSize.u.HighPart)
    {
        return STATUS_INVALID_PARAMETER_3;
    }
    if (SafeMaximumSize.u.LowPart < SafeInitialSize.u.LowPart)
    {
        return STATUS_INVALID_PARAMETER_MIX;
    }

    Status = ProbeAndCaptureUnicodeString(&CapturedFileName,
                                          PreviousMode,
                                          FileName);
    if (!NT_SUCCESS(Status))
    {
        return(Status);
    }

    InitializeObjectAttributes(&ObjectAttributes,
                               &CapturedFileName,
                               OBJ_KERNEL_HANDLE,
                               NULL,
                               NULL);

    Status = IoCreateFile(&FileHandle,
                          FILE_ALL_ACCESS,
                          &ObjectAttributes,
                          &IoStatus,
                          NULL,
                          0,
                          0,
                          FILE_OPEN_IF,
                          FILE_SYNCHRONOUS_IO_NONALERT,
                          NULL,
                          0,
                          CreateFileTypeNone,
                          NULL,
                          SL_OPEN_PAGING_FILE | IO_NO_PARAMETER_CHECKING);

    ReleaseCapturedUnicodeString(&CapturedFileName,
                                 PreviousMode);
    if (!NT_SUCCESS(Status))
    {
        return(Status);
    }

    Status = ZwQueryVolumeInformationFile(FileHandle,
                                          &IoStatus,
                                          &FsSizeInformation,
                                          sizeof(FILE_FS_SIZE_INFORMATION),
                                          FileFsSizeInformation);
    if (!NT_SUCCESS(Status))
    {
        ZwClose(FileHandle);
        return Status;
    }

    BytesPerAllocationUnit = FsSizeInformation.SectorsPerAllocationUnit *
                             FsSizeInformation.BytesPerSector;
    /* FIXME: If we have 2048 BytesPerAllocationUnit (FAT16 < 128MB) there is
     * a problem if the paging file is fragmented. Suppose the first cluster
     * of the paging file is cluster 3042 but cluster 3043 is NOT part of the
     * paging file but of another file. We can't write a complete page (4096
     * bytes) to the physical location of cluster 3042 then. */
    if (BytesPerAllocationUnit % PAGE_SIZE)
    {
        DPRINT1("BytesPerAllocationUnit %lu is not a multiple of PAGE_SIZE %d\n",
                BytesPerAllocationUnit, PAGE_SIZE);
        ZwClose(FileHandle);
        return STATUS_UNSUCCESSFUL;
    }

    Status = ZwSetInformationFile(FileHandle,
                                  &IoStatus,
                                  &SafeInitialSize,
                                  sizeof(LARGE_INTEGER),
                                  FileAllocationInformation);
    if (!NT_SUCCESS(Status))
    {
        ZwClose(FileHandle);
        return(Status);
    }

    Status = ObReferenceObjectByHandle(FileHandle,
                                       FILE_ALL_ACCESS,
                                       IoFileObjectType,
                                       KernelMode,
                                       (PVOID*)&FileObject,
                                       NULL);
    if (!NT_SUCCESS(Status))
    {
        ZwClose(FileHandle);
        return(Status);
    }

    CurrentRetDescList = RetDescList = MmAllocRetrievelDescriptorList(PAIRS_PER_RUN);

    if (CurrentRetDescList == NULL)
    {
        ObDereferenceObject(FileObject);
        ZwClose(FileHandle);
        return(STATUS_NO_MEMORY);
    }

#if defined(__GNUC__)
    Vcn.QuadPart = 0LL;
#else

    Vcn.QuadPart = 0;
#endif

    ExtentCount = 0;
    MaxVcn.QuadPart = (SafeInitialSize.QuadPart + BytesPerAllocationUnit - 1) / BytesPerAllocationUnit;
    while(1)
    {
        Status = ZwFsControlFile(FileHandle,
                                 0,
                                 NULL,
                                 NULL,
                                 &IoStatus,
                                 FSCTL_GET_RETRIEVAL_POINTERS,
                                 &Vcn,
                                 sizeof(LARGE_INTEGER),
                                 &CurrentRetDescList->RetrievalPointers,
                                 sizeof(RETRIEVAL_POINTERS_BUFFER) + PAIRS_PER_RUN * 2 * sizeof(LARGE_INTEGER));
        if (!NT_SUCCESS(Status))
        {
            while (RetDescList)
            {
                CurrentRetDescList = RetDescList;
                RetDescList = RetDescList->Next;
                ExFreePool(CurrentRetDescList);
            }
            ObDereferenceObject(FileObject);
            ZwClose(FileHandle);
            return(Status);
        }
        ExtentCount += CurrentRetDescList->RetrievalPointers.ExtentCount;
        if (CurrentRetDescList->RetrievalPointers.Extents[CurrentRetDescList->RetrievalPointers.ExtentCount-1].NextVcn.QuadPart < MaxVcn.QuadPart)
        {
            CurrentRetDescList->Next = MmAllocRetrievelDescriptorList(PAIRS_PER_RUN);
            if (CurrentRetDescList->Next == NULL)
            {
                while (RetDescList)
                {
                    CurrentRetDescList = RetDescList;
                    RetDescList = RetDescList->Next;
                    ExFreePool(CurrentRetDescList);
                }
                ObDereferenceObject(FileObject);
                ZwClose(FileHandle);
                return(STATUS_NO_MEMORY);
            }
            Vcn = CurrentRetDescList->RetrievalPointers.Extents[CurrentRetDescList->RetrievalPointers.ExtentCount-1].NextVcn;
            CurrentRetDescList = CurrentRetDescList->Next;
        }
        else
        {
            break;
        }
    }

    PagingFile = ExAllocatePool(NonPagedPool, sizeof(*PagingFile));
    if (PagingFile == NULL)
    {
        while (RetDescList)
        {
            CurrentRetDescList = RetDescList;
            RetDescList = RetDescList->Next;
            ExFreePool(CurrentRetDescList);
        }
        ObDereferenceObject(FileObject);
        ZwClose(FileHandle);
        return(STATUS_NO_MEMORY);
    }

    RtlZeroMemory(PagingFile, sizeof(*PagingFile));

    PagingFile->FileObject = FileObject;
    PagingFile->MaximumSize.QuadPart = SafeMaximumSize.QuadPart;
    PagingFile->CurrentSize.QuadPart = SafeInitialSize.QuadPart;
    PagingFile->FreePages = (ULONG)(SafeInitialSize.QuadPart / PAGE_SIZE);
    PagingFile->UsedPages = 0;
    KeInitializeSpinLock(&PagingFile->AllocMapLock);

    AllocMapSize = (PagingFile->FreePages / 32) + 1;
    PagingFile->AllocMap = ExAllocatePool(NonPagedPool,
                                          AllocMapSize * sizeof(ULONG));
    PagingFile->AllocMapSize = AllocMapSize;

    if (PagingFile->AllocMap == NULL)
    {
        while (RetDescList)
        {
            CurrentRetDescList = RetDescList;
            RetDescList = RetDescList->Next;
            ExFreePool(CurrentRetDescList);
        }
        ExFreePool(PagingFile);
        ObDereferenceObject(FileObject);
        ZwClose(FileHandle);
        return(STATUS_NO_MEMORY);
    }
    DPRINT("ExtentCount: %lu\n", ExtentCount);
    Size = sizeof(RETRIEVAL_POINTERS_BUFFER) + ExtentCount * 2 * sizeof(LARGE_INTEGER);
    PagingFile->RetrievalPointers = ExAllocatePool(NonPagedPool, Size);
    if (PagingFile->RetrievalPointers == NULL)
    {
        while (RetDescList)
        {
            CurrentRetDescList = RetDescList;
            RetDescList = RetDescList->Next;
            ExFreePool(CurrentRetDescList);
        }
        ExFreePool(PagingFile->AllocMap);
        ExFreePool(PagingFile);
        ObDereferenceObject(FileObject);
        ZwClose(FileHandle);
        return(STATUS_NO_MEMORY);
    }

    RtlZeroMemory(PagingFile->AllocMap, AllocMapSize * sizeof(ULONG));
    RtlZeroMemory(PagingFile->RetrievalPointers, Size);

    Count = 0;
    PagingFile->RetrievalPointers->ExtentCount = ExtentCount;
    PagingFile->RetrievalPointers->StartingVcn = RetDescList->RetrievalPointers.StartingVcn;
    CurrentRetDescList = RetDescList;
    while (CurrentRetDescList)
    {
        memcpy(&PagingFile->RetrievalPointers->Extents[Count],
               CurrentRetDescList->RetrievalPointers.Extents,
               CurrentRetDescList->RetrievalPointers.ExtentCount * 2 * sizeof(LARGE_INTEGER));
        Count += CurrentRetDescList->RetrievalPointers.ExtentCount;
        RetDescList = CurrentRetDescList;
        CurrentRetDescList = CurrentRetDescList->Next;
        ExFreePool(RetDescList);
    }

    if (PagingFile->RetrievalPointers->ExtentCount != ExtentCount ||
            PagingFile->RetrievalPointers->Extents[ExtentCount - 1].NextVcn.QuadPart != MaxVcn.QuadPart)
    {
        ExFreePool(PagingFile->RetrievalPointers);
        ExFreePool(PagingFile->AllocMap);
        ExFreePool(PagingFile);
        ObDereferenceObject(FileObject);
        ZwClose(FileHandle);
        return(STATUS_UNSUCCESSFUL);
    }

    /*
     * Change the entries from lcn's to volume offset's.
     */
    PagingFile->RetrievalPointers->StartingVcn.QuadPart *= BytesPerAllocationUnit;
    for (i = 0; i < ExtentCount; i++)
    {
        PagingFile->RetrievalPointers->Extents[i].Lcn.QuadPart *= BytesPerAllocationUnit;
        PagingFile->RetrievalPointers->Extents[i].NextVcn.QuadPart *= BytesPerAllocationUnit;
    }

    KeAcquireSpinLock(&PagingFileListLock, &oldIrql);
    for (i = 0; i < MAX_PAGING_FILES; i++)
    {
        if (PagingFileList[i] == NULL)
        {
            PagingFileList[i] = PagingFile;
            break;
        }
    }
    MiFreeSwapPages = MiFreeSwapPages + PagingFile->FreePages;
    MmNumberOfPagingFiles++;
    KeReleaseSpinLock(&PagingFileListLock, oldIrql);

    ZwClose(FileHandle);

    MmSwapSpaceMessage = FALSE;

    return(STATUS_SUCCESS);
}

/* EOF */
