mirror of
https://github.com/reactos/reactos.git
synced 2024-11-18 13:01:40 +00:00
341 lines
9.8 KiB
C
341 lines
9.8 KiB
C
/*
|
|
* Copyright (C) 1998-2005 ReactOS Team (and the authors from the programmers section)
|
|
*
|
|
* 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/cache/section/io.c
|
|
* PURPOSE: Implements section objects
|
|
*
|
|
* PROGRAMMERS: Rex Jolliff
|
|
* David Welch
|
|
* Eric Kohl
|
|
* Emanuele Aliberti
|
|
* Eugene Ingerman
|
|
* Casper Hornstrup
|
|
* KJK::Hyperion
|
|
* Guido de Jong
|
|
* Ge van Geldorp
|
|
* Royce Mitchell III
|
|
* Filip Navara
|
|
* Aleksey Bragin
|
|
* Jason Filby
|
|
* Thomas Weidenmueller
|
|
* Gunnar Andre' Dalsnes
|
|
* Mike Nordell
|
|
* Alex Ionescu
|
|
* Gregor Anich
|
|
* Steven Edwards
|
|
* Herve Poussineau
|
|
*/
|
|
|
|
/* INCLUDES *****************************************************************/
|
|
|
|
#include <ntoskrnl.h>
|
|
#include "newmm.h"
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
#include <reactos/exeformat.h>
|
|
|
|
#if defined (ALLOC_PRAGMA)
|
|
#pragma alloc_text(INIT, MmCreatePhysicalMemorySection)
|
|
#pragma alloc_text(INIT, MmInitSectionImplementation)
|
|
#endif
|
|
|
|
KEVENT CcpLazyWriteEvent;
|
|
|
|
PDEVICE_OBJECT
|
|
NTAPI
|
|
MmGetDeviceObjectForFile(IN PFILE_OBJECT FileObject)
|
|
{
|
|
return IoGetRelatedDeviceObject(FileObject);
|
|
}
|
|
|
|
/*
|
|
|
|
Note:
|
|
This completion function is really required. Paging io completion does almost
|
|
nothing, including freeing the mdls.
|
|
|
|
*/
|
|
_Function_class_(IO_COMPLETION_ROUTINE)
|
|
NTSTATUS
|
|
NTAPI
|
|
MiSimpleReadComplete(PDEVICE_OBJECT DeviceObject,
|
|
PIRP Irp,
|
|
PVOID Context)
|
|
{
|
|
PMDL Mdl = Irp->MdlAddress;
|
|
|
|
/* Unlock MDL Pages, page 167. */
|
|
DPRINT("MiSimpleReadComplete %p\n", Irp);
|
|
while (Mdl)
|
|
{
|
|
DPRINT("MDL Unlock %p\n", Mdl);
|
|
MmUnlockPages(Mdl);
|
|
Mdl = Mdl->Next;
|
|
}
|
|
|
|
/* Check if there's an MDL */
|
|
while ((Mdl = Irp->MdlAddress))
|
|
{
|
|
/* Clear all of them */
|
|
Irp->MdlAddress = Mdl->Next;
|
|
IoFreeMdl(Mdl);
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
|
|
MiSimpleRead is a convenience function that provides either paging or non
|
|
paging reads. The caching and mm systems use this in paging mode, where
|
|
a completion function is required as above. The Paging BOOLEAN determines
|
|
whether the read is issued as a paging read or as an ordinary buffered read.
|
|
|
|
*/
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
MiSimpleRead(PFILE_OBJECT FileObject,
|
|
PLARGE_INTEGER FileOffset,
|
|
PVOID Buffer,
|
|
ULONG Length,
|
|
BOOLEAN Paging,
|
|
PIO_STATUS_BLOCK ReadStatus)
|
|
{
|
|
NTSTATUS Status;
|
|
PIRP Irp = NULL;
|
|
KEVENT ReadWait;
|
|
PDEVICE_OBJECT DeviceObject;
|
|
PIO_STACK_LOCATION IrpSp;
|
|
|
|
ASSERT(FileObject);
|
|
ASSERT(FileOffset);
|
|
ASSERT(Buffer);
|
|
ASSERT(ReadStatus);
|
|
|
|
DeviceObject = MmGetDeviceObjectForFile(FileObject);
|
|
ReadStatus->Status = STATUS_INTERNAL_ERROR;
|
|
ReadStatus->Information = 0;
|
|
|
|
ASSERT(DeviceObject);
|
|
|
|
DPRINT("PAGING READ: FileObject %p <%wZ> Offset %08x%08x Length %ul\n",
|
|
FileObject,
|
|
&FileObject->FileName,
|
|
FileOffset->HighPart,
|
|
FileOffset->LowPart,
|
|
Length);
|
|
|
|
KeInitializeEvent(&ReadWait, NotificationEvent, FALSE);
|
|
|
|
Irp = IoBuildAsynchronousFsdRequest(IRP_MJ_READ,
|
|
DeviceObject,
|
|
Buffer,
|
|
Length,
|
|
FileOffset,
|
|
ReadStatus);
|
|
|
|
if (!Irp)
|
|
{
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
Irp->Flags |= (Paging ? IRP_PAGING_IO | IRP_SYNCHRONOUS_PAGING_IO | IRP_NOCACHE : 0) | IRP_SYNCHRONOUS_API;
|
|
|
|
Irp->UserEvent = &ReadWait;
|
|
Irp->Tail.Overlay.OriginalFileObject = FileObject;
|
|
Irp->Tail.Overlay.Thread = PsGetCurrentThread();
|
|
IrpSp = IoGetNextIrpStackLocation(Irp);
|
|
IrpSp->Control |= SL_INVOKE_ON_SUCCESS | SL_INVOKE_ON_ERROR;
|
|
IrpSp->FileObject = FileObject;
|
|
IrpSp->CompletionRoutine = MiSimpleReadComplete;
|
|
|
|
/* Non paging case, the FileObject will be dereferenced at completion */
|
|
if (!Paging)
|
|
ObReferenceObject(FileObject);
|
|
|
|
Status = IoCallDriver(DeviceObject, Irp);
|
|
if (Status == STATUS_PENDING)
|
|
{
|
|
DPRINT("KeWaitForSingleObject(&ReadWait)\n");
|
|
if (!NT_SUCCESS(KeWaitForSingleObject(&ReadWait,
|
|
Suspended,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL)))
|
|
{
|
|
DPRINT1("Warning: Failed to wait for synchronous IRP\n");
|
|
ASSERT(FALSE);
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
DPRINT("Paging IO Done: %08x\n", ReadStatus->Status);
|
|
/* When "ReadStatus->Information > 0" is false and "ReadStatus->Status == STATUS_END_OF_FILE" is true
|
|
* it means that read pointer is out of file, so we must fail */
|
|
Status = ReadStatus->Status == STATUS_END_OF_FILE && ReadStatus->Information > 0 ? STATUS_SUCCESS : ReadStatus->Status;
|
|
return Status;
|
|
}
|
|
|
|
/*
|
|
|
|
Convenience function for writing from kernel space. This issues a paging
|
|
write in all cases.
|
|
|
|
*/
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
_MiSimpleWrite(PFILE_OBJECT FileObject,
|
|
PLARGE_INTEGER FileOffset,
|
|
PVOID Buffer,
|
|
ULONG Length,
|
|
PIO_STATUS_BLOCK ReadStatus,
|
|
const char *File,
|
|
int Line)
|
|
{
|
|
NTSTATUS Status;
|
|
PIRP Irp = NULL;
|
|
KEVENT ReadWait;
|
|
PDEVICE_OBJECT DeviceObject;
|
|
PIO_STACK_LOCATION IrpSp;
|
|
|
|
ASSERT(FileObject);
|
|
ASSERT(FileOffset);
|
|
ASSERT(Buffer);
|
|
ASSERT(ReadStatus);
|
|
|
|
DeviceObject = MmGetDeviceObjectForFile(FileObject);
|
|
ASSERT(DeviceObject);
|
|
|
|
DPRINT("PAGING WRITE: FileObject %p <%wZ> Offset 0x%I64x Length %lu (%s:%d)\n",
|
|
FileObject,
|
|
&FileObject->FileName,
|
|
FileOffset->QuadPart,
|
|
Length,
|
|
File,
|
|
Line);
|
|
|
|
KeInitializeEvent(&ReadWait, NotificationEvent, FALSE);
|
|
|
|
Irp = IoBuildAsynchronousFsdRequest(IRP_MJ_WRITE,
|
|
DeviceObject,
|
|
Buffer,
|
|
Length,
|
|
FileOffset,
|
|
ReadStatus);
|
|
|
|
if (!Irp)
|
|
{
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
Irp->Flags = IRP_PAGING_IO | IRP_SYNCHRONOUS_PAGING_IO | IRP_NOCACHE | IRP_SYNCHRONOUS_API;
|
|
|
|
Irp->UserEvent = &ReadWait;
|
|
Irp->Tail.Overlay.OriginalFileObject = FileObject;
|
|
Irp->Tail.Overlay.Thread = PsGetCurrentThread();
|
|
IrpSp = IoGetNextIrpStackLocation(Irp);
|
|
IrpSp->Control |= SL_INVOKE_ON_SUCCESS | SL_INVOKE_ON_ERROR;
|
|
IrpSp->FileObject = FileObject;
|
|
IrpSp->CompletionRoutine = MiSimpleReadComplete;
|
|
|
|
DPRINT("Call Driver\n");
|
|
Status = IoCallDriver(DeviceObject, Irp);
|
|
DPRINT("Status %x\n", Status);
|
|
|
|
if (Status == STATUS_PENDING)
|
|
{
|
|
DPRINT("KeWaitForSingleObject(&ReadWait)\n");
|
|
if (!NT_SUCCESS(KeWaitForSingleObject(&ReadWait,
|
|
Suspended,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL)))
|
|
{
|
|
DPRINT1("Warning: Failed to wait for synchronous IRP\n");
|
|
ASSERT(FALSE);
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
DPRINT("Paging IO Done: %08x\n", ReadStatus->Status);
|
|
return ReadStatus->Status;
|
|
}
|
|
|
|
extern KEVENT MpwThreadEvent;
|
|
FAST_MUTEX MiWriteMutex;
|
|
|
|
/*
|
|
|
|
Function which uses MiSimpleWrite to write back a single page to a file.
|
|
The page in question does not need to be mapped. This function could be
|
|
made a bit more efficient by avoiding the copy and making a system space
|
|
mdl.
|
|
|
|
*/
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
_MiWriteBackPage(PFILE_OBJECT FileObject,
|
|
PLARGE_INTEGER FileOffset,
|
|
ULONG Length,
|
|
PFN_NUMBER Page,
|
|
const char *File,
|
|
int Line)
|
|
{
|
|
NTSTATUS Status;
|
|
PVOID Hyperspace;
|
|
IO_STATUS_BLOCK Iosb;
|
|
KIRQL OldIrql;
|
|
PVOID PageBuffer = ExAllocatePool(NonPagedPool, PAGE_SIZE);
|
|
|
|
if (!PageBuffer) return STATUS_NO_MEMORY;
|
|
|
|
Hyperspace = MiMapPageInHyperSpace(PsGetCurrentProcess(), Page, &OldIrql);
|
|
if (!Hyperspace)
|
|
{
|
|
ExFreePool(PageBuffer);
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
RtlCopyMemory(PageBuffer, Hyperspace, PAGE_SIZE);
|
|
MiUnmapPageInHyperSpace(PsGetCurrentProcess(), Hyperspace, OldIrql);
|
|
|
|
DPRINT("MiWriteBackPage(%wZ,%08x%08x,%s:%d)\n",
|
|
&FileObject->FileName,
|
|
FileOffset->u.HighPart,
|
|
FileOffset->u.LowPart,
|
|
File,
|
|
Line);
|
|
|
|
Status = MiSimpleWrite(FileObject,
|
|
FileOffset,
|
|
PageBuffer,
|
|
Length,
|
|
&Iosb);
|
|
|
|
ExFreePool(PageBuffer);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("MiSimpleWrite failed (%x)\n", Status);
|
|
}
|
|
|
|
return Status;
|
|
}
|