NTSTATUS
NtCreatePagingFile (
__in PUNICODE_STRING PageFileName,
__in PLARGE_INTEGER MinimumSize,
__in PLARGE_INTEGER MaximumSize,
__in ULONG Priority
)
/*++
Routine Description:
This routine opens the specified file, attempts to write a page
to the specified file, and creates the necessary structures to
use the file as a paging file.
If this file is the first paging file, the modified page writer
is started.
This system service requires the caller to have SeCreatePagefilePrivilege.
Arguments:
PageFileName - Supplies the fully qualified file name.
MinimumSize - Supplies the starting size of the paging file.
This value is rounded up to the host page size.
MaximumSize - Supplies the maximum number of bytes to write to the file.
This value is rounded up to the host page size.
Priority - Supplies the relative priority of this paging file.
--*/
{
ULONG i;
PFILE_OBJECT File;
NTSTATUS Status;
OBJECT_ATTRIBUTES PagingFileAttributes;
HANDLE FileHandle;
IO_STATUS_BLOCK IoStatus;
UNICODE_STRING CapturedName;
PWSTR CapturedBuffer;
LARGE_INTEGER CapturedMaximumSize;
LARGE_INTEGER CapturedMinimumSize;
FILE_END_OF_FILE_INFORMATION EndOfFileInformation;
KPROCESSOR_MODE PreviousMode;
FILE_FS_DEVICE_INFORMATION FileDeviceInfo;
ULONG ReturnedLength;
ULONG PageFileNumber;
ULONG NewMaxSizeInPages;
ULONG NewMinSizeInPages;
PMMPAGING_FILE FoundExisting;
PMMPAGING_FILE NewPagingFile;
PRTL_BITMAP NewBitmap;
PDEVICE_OBJECT deviceObject;
MMPAGE_FILE_EXPANSION PageExtend;
SECURITY_DESCRIPTOR SecurityDescriptor;
ULONG DaclLength;
PACL Dacl;
DBG_UNREFERENCED_PARAMETER (Priority);
PAGED_CODE();
CapturedBuffer = NULL;
Dacl = NULL;
if (MmNumberOfPagingFiles == MAX_PAGE_FILES) {
//
// The maximum number of paging files is already in use.
//
return STATUS_TOO_MANY_PAGING_FILES;
}
PreviousMode = KeGetPreviousMode();
if (PreviousMode != KernelMode) {
//
// Make sure the caller has the proper privilege for this.
//
if (!SeSinglePrivilegeCheck (SeCreatePagefilePrivilege, PreviousMode)) {
return STATUS_PRIVILEGE_NOT_HELD;
}
//
// Probe arguments.
//
try {
#if !defined (_WIN64)
//
// Note we only probe for byte alignment because early releases
// of NT did and we don't want to break user apps
// that had bad alignment if they worked before.
//
ProbeForReadSmallStructure (PageFileName,
sizeof(*PageFileName),
sizeof(UCHAR));
#else
ProbeForReadSmallStructure (PageFileName,
sizeof(*PageFileName),
PROBE_ALIGNMENT (UNICODE_STRING));
#endif
ProbeForReadSmallStructure (MaximumSize,
sizeof(LARGE_INTEGER),
PROBE_ALIGNMENT (LARGE_INTEGER));
ProbeForReadSmallStructure (MinimumSize,
sizeof(LARGE_INTEGER),
PROBE_ALIGNMENT (LARGE_INTEGER));
//
// Capture arguments.
//
CapturedMinimumSize = *MinimumSize;
} except (EXCEPTION_EXECUTE_HANDLER) {
//
// If an exception occurs during the probe or capture
// of the initial values, then handle the exception and
// return the exception code as the status value.
//
return GetExceptionCode();
}
}
else {
//
// Capture arguments.
//
CapturedMinimumSize = *MinimumSize;
}
if ((CapturedMinimumSize.QuadPart > MI_MAXIMUM_PAGEFILE_SIZE) ||
(CapturedMinimumSize.LowPart < MINIMUM_PAGE_FILE_SIZE)) {
return STATUS_INVALID_PARAMETER_2;
}
if (PreviousMode != KernelMode) {
try {
CapturedMaximumSize = *MaximumSize;
} except (EXCEPTION_EXECUTE_HANDLER) {
//
// If an exception occurs during the probe or capture
// of the initial values, then handle the exception and
// return the exception code as the status value.
//
return GetExceptionCode();
}
}
else {
CapturedMaximumSize = *MaximumSize;
}
if (CapturedMaximumSize.QuadPart > MI_MAXIMUM_PAGEFILE_SIZE) {
return STATUS_INVALID_PARAMETER_3;
}
if (CapturedMinimumSize.QuadPart > CapturedMaximumSize.QuadPart) {
return STATUS_INVALID_PARAMETER_3;
}
if (PreviousMode != KernelMode) {
try {
CapturedName = *PageFileName;
} except (EXCEPTION_EXECUTE_HANDLER) {
//
// If an exception occurs during the probe or capture
// of the initial values, then handle the exception and
// return the exception code as the status value.
//
return GetExceptionCode();
}
}
else {
CapturedName = *PageFileName;
}
CapturedName.MaximumLength = CapturedName.Length;
if ((CapturedName.Length == 0) ||
(CapturedName.Length > MAXIMUM_FILENAME_LENGTH )) {
return STATUS_OBJECT_NAME_INVALID;
}
CapturedBuffer = ExAllocatePoolWithTag (PagedPool,
(ULONG)CapturedName.Length,
' mM');
if (CapturedBuffer == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
if (PreviousMode != KernelMode) {
try {
ProbeForRead (CapturedName.Buffer,
CapturedName.Length,
sizeof (UCHAR));
//
// Copy the string to the allocated buffer.
//
RtlCopyMemory (CapturedBuffer,
CapturedName.Buffer,
CapturedName.Length);
} except (EXCEPTION_EXECUTE_HANDLER) {
//
// If an exception occurs during the probe or capture
// of the initial values, then handle the exception and
// return the exception code as the status value.
//
ExFreePool (CapturedBuffer);
return GetExceptionCode();
}
}
else {
//
// Copy the string to the allocated buffer.
//
RtlCopyMemory (CapturedBuffer,
CapturedName.Buffer,
CapturedName.Length);
}
//
// Point the buffer to the string that was just copied.
//
CapturedName.Buffer = CapturedBuffer;
//
// Create a security descriptor to protect the pagefile
//
Status = RtlCreateSecurityDescriptor (&SecurityDescriptor,
SECURITY_DESCRIPTOR_REVISION);
if (!NT_SUCCESS (Status)) {
goto ErrorReturn1;
}
DaclLength = sizeof (ACL) + sizeof (ACCESS_ALLOWED_ACE) * 2 +
RtlLengthSid (SeLocalSystemSid) +
RtlLengthSid (SeAliasAdminsSid);
Dacl = ExAllocatePoolWithTag (PagedPool, DaclLength, 'lcaD');
if (Dacl == NULL) {
Status = STATUS_INSUFFICIENT_RESOURCES;
goto ErrorReturn1;
}
Status = RtlCreateAcl (Dacl, DaclLength, ACL_REVISION);
if (!NT_SUCCESS (Status)) {
goto ErrorReturn1;
}
Status = RtlAddAccessAllowedAce (Dacl,
ACL_REVISION,
FILE_ALL_ACCESS,
SeAliasAdminsSid);
if (!NT_SUCCESS (Status)) {
goto ErrorReturn1;
}
Status = RtlAddAccessAllowedAce (Dacl,
ACL_REVISION,
FILE_ALL_ACCESS,
SeLocalSystemSid);
if (!NT_SUCCESS (Status)) {
goto ErrorReturn1;
}
Status = RtlSetDaclSecurityDescriptor (&SecurityDescriptor,
TRUE,
Dacl,
FALSE);
if (!NT_SUCCESS (Status)) {
goto ErrorReturn1;
}
//
// Open a paging file and get the size.
//
InitializeObjectAttributes (&PagingFileAttributes,
&CapturedName,
(OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE),
NULL,
&SecurityDescriptor);
//
// Note this macro cannot use ULONG_PTR as it must also work on PAE.
//
#define ROUND64_TO_PAGES(Size) (((ULONG64)(Size) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))
EndOfFileInformation.EndOfFile.QuadPart =
ROUND64_TO_PAGES (CapturedMinimumSize.QuadPart);
Status = IoCreateFile (&FileHandle,
FILE_READ_DATA | FILE_WRITE_DATA | WRITE_DAC | SYNCHRONIZE,
&PagingFileAttributes,
&IoStatus,
&CapturedMinimumSize,
FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM,
FILE_SHARE_WRITE,
FILE_SUPERSEDE,
FILE_NO_INTERMEDIATE_BUFFERING | FILE_NO_COMPRESSION | FILE_DELETE_ON_CLOSE,
NULL,
0L,
CreateFileTypeNone,
NULL,
IO_OPEN_PAGING_FILE | IO_NO_PARAMETER_CHECKING);
if (NT_SUCCESS(Status)) {
//
// Update the DACL in case there was a pre-existing regular file named
// pagefile.sys (even supersede above does not do this).
//
if (NT_SUCCESS(IoStatus.Status)) {
Status = ZwSetSecurityObject (FileHandle,
DACL_SECURITY_INFORMATION,
&SecurityDescriptor);
if (!NT_SUCCESS(Status)) {
goto ErrorReturn2;
}
}
}
else {
//
// Treat this as an extension of an existing pagefile maximum -
// and try to open rather than create the paging file specified.
//
Status = IoCreateFile (&FileHandle,
FILE_WRITE_DATA | SYNCHRONIZE,
&PagingFileAttributes,
&IoStatus,
&CapturedMinimumSize,
FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM,
FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_OPEN,
FILE_NO_INTERMEDIATE_BUFFERING | FILE_NO_COMPRESSION,
(PVOID) NULL,
0L,
CreateFileTypeNone,
(PVOID) NULL,
IO_OPEN_PAGING_FILE | IO_NO_PARAMETER_CHECKING);
if (!NT_SUCCESS(Status)) {
#if DBG
if (Status != STATUS_DISK_FULL) {
DbgPrintEx (DPFLTR_MM_ID, DPFLTR_INFO_LEVEL,
"MM MODWRITE: unable to open paging file %wZ - status = %X \n", &CapturedName, Status);
}
#endif
goto ErrorReturn1;
}
Status = ObReferenceObjectByHandle (FileHandle,
FILE_READ_DATA | FILE_WRITE_DATA,
IoFileObjectType,
KernelMode,
(PVOID *)&File,
NULL);
if (!NT_SUCCESS(Status)) {
goto ErrorReturn2;
}
FoundExisting = NULL;
KeAcquireGuardedMutex (&MmPageFileCreationLock);
for (PageFileNumber = 0; PageFileNumber < MmNumberOfPagingFiles; PageFileNumber += 1) {
if (MmPagingFile[PageFileNumber]->File->SectionObjectPointer == File->SectionObjectPointer) {
FoundExisting = MmPagingFile[PageFileNumber];
break;
}
}
if (FoundExisting == NULL) {
Status = STATUS_NOT_FOUND;
goto ErrorReturn4;
}
//
// Check for increases in the minimum or the maximum paging file sizes.
// Decreasing either paging file size on the fly is not allowed.
//
NewMaxSizeInPages = (ULONG)(CapturedMaximumSize.QuadPart >> PAGE_SHIFT);
NewMinSizeInPages = (ULONG)(CapturedMinimumSize.QuadPart >> PAGE_SHIFT);
if (FoundExisting->MinimumSize > NewMinSizeInPages) {
Status = STATUS_INVALID_PARAMETER_2;
goto ErrorReturn4;
}
if (FoundExisting->MaximumSize > NewMaxSizeInPages) {
Status = STATUS_INVALID_PARAMETER_3;
goto ErrorReturn4;
}
if (NewMaxSizeInPages > FoundExisting->MaximumSize) {
//
// Make sure that the pagefile increase doesn't cause the commit
// limit (in pages) to wrap. Currently this can only happen on
// PAE systems where 16 pagefiles of 16TB (==256TB) is greater
// than the 32-bit commit variable (max is 16TB).
//
if (MmTotalCommitLimitMaximum + (NewMaxSizeInPages - FoundExisting->MaximumSize) <= MmTotalCommitLimitMaximum) {
Status = STATUS_INVALID_PARAMETER_3;
goto ErrorReturn4;
}
//
// Handle the increase to the maximum paging file size.
//
MiCreateBitMap (&NewBitmap, NewMaxSizeInPages, NonPagedPool);
if (NewBitmap == NULL) {
Status = STATUS_INSUFFICIENT_RESOURCES;
goto ErrorReturn4;
}
MiExtendPagingFileMaximum (PageFileNumber, NewBitmap);
//
// We may be low on commitment and/or may have put a temporary
// stopgate on things. Clear up the logjam now by forcing an
// extension and immediately returning it.
//
if (MmTotalCommittedPages + 100 > MmTotalCommitLimit) {
if (MiChargeCommitment (200, NULL) == TRUE) {
MiReturnCommitment (200);
}
}
}
if (NewMinSizeInPages > FoundExisting->MinimumSize) {
//
// Handle the increase to the minimum paging file size.
//
if (NewMinSizeInPages > FoundExisting->Size) {
//
// Queue a message to the segment dereferencing / pagefile
// extending thread to see if the page file can be extended.
//
PageExtend.InProgress = 1;
PageExtend.ActualExpansion = 0;
PageExtend.RequestedExpansionSize = NewMinSizeInPages - FoundExisting->Size;
PageExtend.Segment = NULL;
PageExtend.PageFileNumber = PageFileNumber;
KeInitializeEvent (&PageExtend.Event, NotificationEvent, FALSE);
MiIssuePageExtendRequest (&PageExtend);
}
//
// The current size is now greater than the new desired minimum.
// Ensure subsequent contractions obey this new minimum.
//
if (FoundExisting->Size >= NewMinSizeInPages) {
ASSERT (FoundExisting->Size >= FoundExisting->MinimumSize);
ASSERT (NewMinSizeInPages >= FoundExisting->MinimumSize);
FoundExisting->MinimumSize = NewMinSizeInPages;
}
else {
//
// The pagefile could not be expanded to handle the new minimum.
// No easy way to undo any maximum raising that may have been
// done as the space may have already been used, so just set
// Status so our caller knows it didn't all go perfectly.
//
Status = STATUS_INSUFFICIENT_RESOURCES;
}
}
goto ErrorReturn4;
}
//
// Free the DACL as it's no longer needed.
//
ExFreePool (Dacl);
Dacl = NULL;
if (!NT_SUCCESS(IoStatus.Status)) {
KdPrint(("MM MODWRITE: unable to open paging file %wZ - iosb %lx\n", &CapturedName, IoStatus.Status));
Status = IoStatus.Status;
goto ErrorReturn1;
}
//
// Make sure that the pagefile increase doesn't cause the commit
// limit (in pages) to wrap. Currently this can only happen on
// PAE systems where 16 pagefiles of 16TB (==256TB) is greater
// than the 32-bit commit variable (max is 16TB).
//
if (MmTotalCommitLimitMaximum + (CapturedMaximumSize.QuadPart >> PAGE_SHIFT)
<= MmTotalCommitLimitMaximum) {
Status = STATUS_INVALID_PARAMETER_3;
goto ErrorReturn2;
}
Status = ZwSetInformationFile (FileHandle,
&IoStatus,
&EndOfFileInformation,
sizeof(EndOfFileInformation),
FileEndOfFileInformation);
if (!NT_SUCCESS(Status)) {
KdPrint(("MM MODWRITE: unable to set length of paging file %wZ status = %X \n",
&CapturedName, Status));
goto ErrorReturn2;
}
if (!NT_SUCCESS(IoStatus.Status)) {
KdPrint(("MM MODWRITE: unable to set length of paging file %wZ - iosb %lx\n",
&CapturedName, IoStatus.Status));
Status = IoStatus.Status;
goto ErrorReturn2;
}
Status = ObReferenceObjectByHandle ( FileHandle,
FILE_READ_DATA | FILE_WRITE_DATA,
IoFileObjectType,
KernelMode,
(PVOID *)&File,
NULL );
if (!NT_SUCCESS(Status)) {
KdPrint(("MM MODWRITE: Unable to reference paging file - %wZ\n",
&CapturedName));
goto ErrorReturn2;
}
//
// Get the address of the target device object and ensure
// the specified file is of a suitable type.
//
deviceObject = IoGetRelatedDeviceObject (File);
if ((deviceObject->DeviceType != FILE_DEVICE_DISK_FILE_SYSTEM) &&
(deviceObject->DeviceType != FILE_DEVICE_NETWORK_FILE_SYSTEM) &&
(deviceObject->DeviceType != FILE_DEVICE_DFS_VOLUME) &&
(deviceObject->DeviceType != FILE_DEVICE_DFS_FILE_SYSTEM)) {
KdPrint(("MM MODWRITE: Invalid paging file type - %x\n",
deviceObject->DeviceType));
Status = STATUS_UNRECOGNIZED_VOLUME;
goto ErrorReturn3;
}
//
// Make sure the specified file is not currently being used
// as a mapped data file.
//
Status = MiCheckPageFileMapping (File);
if (!NT_SUCCESS(Status)) {
goto ErrorReturn3;
}
//
// Make sure the volume is not a floppy disk.
//
Status = IoQueryVolumeInformation ( File,
FileFsDeviceInformation,
sizeof(FILE_FS_DEVICE_INFORMATION),
&FileDeviceInfo,
&ReturnedLength
);
if (FILE_FLOPPY_DISKETTE & FileDeviceInfo.Characteristics) {
Status = STATUS_FLOPPY_VOLUME;
goto ErrorReturn3;
}
//
// Check with all of the drivers along the path to the file to ensure
// that they are willing to follow the rules required of them and to
// give them a chance to lock down code and data that needs to be locked.
// If any of the drivers along the path refuses to participate, fail the
// pagefile creation.
//
Status = PpPagePathAssign (File);
if (!NT_SUCCESS(Status)) {
KdPrint(( "PpPagePathAssign(%wZ) FAILED: %x\n", &CapturedName, Status ));
//
// Fail the pagefile creation if the storage stack tells us to.
//
goto ErrorReturn3;
}
NewPagingFile = ExAllocatePoolWithTag (NonPagedPool,
sizeof(MMPAGING_FILE),
' mM');
if (NewPagingFile == NULL) {
//
// Allocate pool failed.
//
Status = STATUS_INSUFFICIENT_RESOURCES;
goto ErrorReturn3;
}
RtlZeroMemory (NewPagingFile, sizeof(MMPAGING_FILE));
NewPagingFile->File = File;
NewPagingFile->FileHandle = FileHandle;
NewPagingFile->Size = (PFN_NUMBER)(CapturedMinimumSize.QuadPart >> PAGE_SHIFT);
NewPagingFile->MinimumSize = NewPagingFile->Size;
NewPagingFile->FreeSpace = NewPagingFile->Size - 1;
NewPagingFile->MaximumSize = (PFN_NUMBER)(CapturedMaximumSize.QuadPart >>
PAGE_SHIFT);
for (i = 0; i < MM_PAGING_FILE_MDLS; i += 1) {
NewPagingFile->Entry[i] = ExAllocatePoolWithTag (NonPagedPool,
sizeof(MMMOD_WRITER_MDL_ENTRY) +
MmModifiedWriteClusterSize *
sizeof(PFN_NUMBER),
' mM');
if (NewPagingFile->Entry[i] == NULL) {
//
// Allocate pool failed.
//
while (i != 0) {
i -= 1;
ExFreePool (NewPagingFile->Entry[i]);
}
ExFreePool (NewPagingFile);
Status = STATUS_INSUFFICIENT_RESOURCES;
goto ErrorReturn3;
}
RtlZeroMemory (NewPagingFile->Entry[i], sizeof(MMMOD_WRITER_MDL_ENTRY));
NewPagingFile->Entry[i]->PagingListHead = &MmPagingFileHeader;
NewPagingFile->Entry[i]->PagingFile = NewPagingFile;
}
NewPagingFile->PageFileName = CapturedName;
MiCreateBitMap (&NewPagingFile->Bitmap,
NewPagingFile->MaximumSize,
NonPagedPool);
if (NewPagingFile->Bitmap == NULL) {
//
// Allocate pool failed.
//
ExFreePool (NewPagingFile->Entry[0]);
ExFreePool (NewPagingFile->Entry[1]);
ExFreePool (NewPagingFile);
Status = STATUS_INSUFFICIENT_RESOURCES;
goto ErrorReturn3;
}
Status = MiZeroPageFileFirstPage (File);
if (!NT_SUCCESS (Status)) {
//
// The storage stack could not zero the first page of the file.
// This means an old crashdump signature could still be around so
// fail the create.
//
for (i = 0; i < MM_PAGING_FILE_MDLS; i += 1) {
ExFreePool (NewPagingFile->Entry[i]);
}
ExFreePool (NewPagingFile);
MiRemoveBitMap (&NewPagingFile->Bitmap);
goto ErrorReturn3;
}
RtlSetAllBits (NewPagingFile->Bitmap);
//
// Set the first bit as 0 is an invalid page location, clear the
// following bits.
//
RtlClearBits (NewPagingFile->Bitmap,
1,
(ULONG)(NewPagingFile->Size - 1));
//
// See if this pagefile is on the boot partition, and if so, mark it
// so we can find it later if someone enables crashdump.
//
if (File->DeviceObject->Flags & DO_SYSTEM_BOOT_PARTITION) {
NewPagingFile->BootPartition = 1;
}
else {
NewPagingFile->BootPartition = 0;
}
//
// Acquire the global page file creation mutex.
//
KeAcquireGuardedMutex (&MmPageFileCreationLock);
PageFileNumber = MmNumberOfPagingFiles;
MmPagingFile[PageFileNumber] = NewPagingFile;
NewPagingFile->PageFileNumber = PageFileNumber;
MiInsertPageFileInList ();
if (PageFileNumber == 0) {
//
// The first paging file has been created and reservation of any
// crashdump pages has completed, signal the modified
// page writer.
//
MiReleaseModifiedWriter ();
}
KeReleaseGuardedMutex (&MmPageFileCreationLock);
//
// Note that the file handle (a kernel handle) is not closed during the
// create path (it IS duped and closed in the pagefile size extending path)
// to prevent the paging file from being deleted or opened again. It is
// also kept open so that extensions of existing pagefiles can be detected
// because successive IoCreateFile calls will fail.
//
if ((!MmSystemPageFileLocated) &&
(File->DeviceObject->Flags & DO_SYSTEM_BOOT_PARTITION)) {
MmSystemPageFileLocated = IoInitializeCrashDump (FileHandle);
}
return STATUS_SUCCESS;
//
// Error returns:
//
ErrorReturn4:
KeReleaseGuardedMutex (&MmPageFileCreationLock);
ErrorReturn3:
ObDereferenceObject (File);
ErrorReturn2:
ZwClose (FileHandle);
ErrorReturn1:
if (Dacl != NULL) {
ExFreePool (Dacl);
}
ExFreePool (CapturedBuffer);
return Status;
}