mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2848 lines
81 KiB
2848 lines
81 KiB
/*++
|
|
|
|
Copyright (c) 1990 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
readwrit.c
|
|
|
|
Abstract:
|
|
|
|
This module implements the NtReadFile and NtWriteFile APIs in the
|
|
NT Lan Manager redirector.
|
|
|
|
|
|
Author:
|
|
|
|
Larry Osterman (LarryO) 15-Aug-1990
|
|
|
|
Revision History:
|
|
|
|
15-Aug-1990 LarryO
|
|
|
|
Created
|
|
|
|
--*/
|
|
|
|
#define INCLUDE_SMB_READ_WRITE
|
|
#define INCLUDE_SMB_RAW
|
|
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
//
|
|
// If the server supports large Read&X commands, this is the largest we'll
|
|
// actually ask for
|
|
//
|
|
#define LARGEST_READANDX (60*1024)
|
|
|
|
typedef
|
|
struct _READANDXCONTEXT {
|
|
TRANCEIVE_HEADER Header; // Common header structure
|
|
PIRP ReceiveIrp; // IRP used for receive if specified
|
|
PMDL DataMdl; // MDL mapped into user's buffer.
|
|
PSMB_BUFFER ReceiveSmbBuffer; // SMB buffer for receive
|
|
KEVENT ReceiveCompleteEvent; // Event set when receive completes.
|
|
ULONG ReceiveLength; // Number of bytes finally received.
|
|
ULONG BytesReceived;
|
|
ULONG BytesRemainingToBeRead;
|
|
BOOLEAN ReceivePosted; // True if receive was posted.
|
|
} READ_ANDX_CONTEXT, *PREAD_ANDX_CONTEXT;
|
|
|
|
#ifdef PAGING_OVER_THE_NET
|
|
NTSTATUS
|
|
RdrPagingRead(
|
|
IN PIRP Irp,
|
|
IN PICB Icb,
|
|
IN PMDL MdlAddress,
|
|
IN PLARGE_INTEGER ByteOffset,
|
|
IN ULONG Length,
|
|
IN PULONG TotalDataRead
|
|
);
|
|
#endif
|
|
|
|
DBGSTATIC
|
|
NTSTATUS
|
|
RawRead (
|
|
IN PIRP Irp,
|
|
IN PICB Icb,
|
|
IN LARGE_INTEGER ReadOffset,
|
|
IN ULONG Length,
|
|
IN ULONG TotalDataReadSoFar,
|
|
OUT PBOOLEAN AllDataRead,
|
|
OUT PULONG AmountActuallyRead
|
|
);
|
|
|
|
DBGSTATIC
|
|
NTSTATUS
|
|
CoreRead
|
|
(
|
|
IN PIRP Irp OPTIONAL,
|
|
IN PICB Icb,
|
|
IN ULONG Length,
|
|
IN LARGE_INTEGER ReadOffset,
|
|
IN ULONG TotalDataReadSoFar,
|
|
OUT PBOOLEAN AllDataRead,
|
|
OUT PULONG AmountActuallyRead
|
|
);
|
|
|
|
DBGSTATIC
|
|
NTSTATUS
|
|
ReadAndX (
|
|
IN PIRP Irp,
|
|
IN PICB Icb,
|
|
IN ULONG Length,
|
|
IN LARGE_INTEGER ReadOffset,
|
|
IN ULONG TotalReadSoFar,
|
|
OUT PBOOLEAN AllDataRead,
|
|
OUT PULONG AmountActuallyRead,
|
|
OUT PULONG BytesRemainingToBeRead OPTIONAL
|
|
);
|
|
|
|
STANDARD_CALLBACK_HEADER(
|
|
ReadAndXCallback
|
|
);
|
|
|
|
DBGSTATIC
|
|
NTSTATUS
|
|
ReadAndXComplete (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Ctx
|
|
);
|
|
|
|
NTSTATUS
|
|
RdrCheckCanceledIrp(
|
|
IN PIRP Irp
|
|
);
|
|
|
|
VOID
|
|
RdrUpdateNextReadOffset(
|
|
IN PICB Icb,
|
|
IN LARGE_INTEGER IOOffset
|
|
);
|
|
|
|
LARGE_INTEGER
|
|
RdrQueryFileSize(
|
|
IN PFCB Fcb
|
|
);
|
|
|
|
VOID
|
|
RdrQueryFileSizes(
|
|
IN PFCB Fcb,
|
|
OUT PLARGE_INTEGER FileSize,
|
|
OUT PLARGE_INTEGER ValidDataLength OPTIONAL,
|
|
OUT PLARGE_INTEGER FileAllocation OPTIONAL
|
|
);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, CoreRead)
|
|
#pragma alloc_text(PAGE, RawRead)
|
|
|
|
#ifndef PAGING_OVER_THE_NET
|
|
#pragma alloc_text(PAGE, RdrFsdRead)
|
|
#pragma alloc_text(PAGE, RdrFspRead)
|
|
#pragma alloc_text(PAGE, RdrFscRead)
|
|
#pragma alloc_text(PAGE, ReadAndX)
|
|
#pragma alloc_text(PAGE, RdrQueryFileSize)
|
|
#pragma alloc_text(PAGE, RdrQueryFileSizes)
|
|
#endif
|
|
|
|
#pragma alloc_text(PAGE3FILE, RdrUpdateNextReadOffset)
|
|
#pragma alloc_text(PAGE3FILE, RdrCheckCanceledIrp)
|
|
#pragma alloc_text(PAGE3FILE, ReadAndXCallback)
|
|
#pragma alloc_text(PAGE3FILE, ReadAndXComplete)
|
|
#endif
|
|
|
|
NTSTATUS
|
|
RdrFsdRead (
|
|
IN PFS_DEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine processes the NtRead request in the redirector FSD.
|
|
|
|
Arguments:
|
|
|
|
DriverObject - Supplies a pointer to the redirector driver object.
|
|
Irp - Supplies a pointer to the IRP to be processed.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - The FSD status for this Irp.
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
PFILE_OBJECT FileObject = IrpSp->FileObject;
|
|
PICB Icb = FileObject->FsContext2;
|
|
|
|
#ifndef PAGING_OVER_THE_NET
|
|
PAGED_CODE();
|
|
#endif
|
|
|
|
FsRtlEnterFileSystem();
|
|
|
|
#if 0 && RDRDBG_LOG
|
|
{
|
|
LARGE_INTEGER tick;
|
|
KeQueryTickCount(&tick);
|
|
//RdrLog(( "read", &Icb->Fcb->FileName, 2, tick.LowPart, tick.HighPart ));
|
|
//RdrLog(( "read", &Icb->Fcb->FileName, 4, IoGetRequestorProcess(Irp), IrpSp->FileObject,
|
|
// IrpSp->Parameters.Read.ByteOffset.LowPart,
|
|
// IrpSp->Parameters.Read.Length | (FlagOn(Irp->Flags,IRP_PAGING_IO) ? 0x80000000 : 0)));
|
|
}
|
|
#endif
|
|
|
|
ASSERT(Icb->Signature == STRUCTURE_SIGNATURE_ICB);
|
|
|
|
dprintf(DPRT_DISPATCH, ("NtReadFile..\nFile %wZ, Read %ld bytes at %lx%lx\n",
|
|
&Icb->Fcb->FileName, IrpSp->Parameters.Read.Length,
|
|
IrpSp->Parameters.Read.ByteOffset.HighPart,
|
|
IrpSp->Parameters.Read.ByteOffset.LowPart));
|
|
|
|
|
|
RdrStatistics.ReadOperations += 1;
|
|
|
|
//
|
|
// The non NT SMB protocol does not support reads at offsets greater than
|
|
// 32 bits into the file, so disallow any and all ops that will go longer
|
|
// than 32 bits into the file.
|
|
//
|
|
|
|
if ((IrpSp->Parameters.Read.ByteOffset.HighPart != 0) &&
|
|
!(Icb->Fcb->Connection->Server->Capabilities & DF_LARGE_FILES) &&
|
|
(Icb->Type == DiskFile)) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
RdrCompleteRequest(Irp, Status);
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Early out on read requests for 0 bytes.
|
|
//
|
|
|
|
if (IrpSp->Parameters.Read.Length==0) {
|
|
Status = STATUS_SUCCESS;
|
|
Irp->IoStatus.Information = 0;
|
|
RdrCompleteRequest(Irp, Status);
|
|
FsRtlExitFileSystem();
|
|
return Status;
|
|
}
|
|
|
|
try {
|
|
|
|
//
|
|
// Pass the request onto common routine and process the request.
|
|
//
|
|
// If necessary, process the request in the FSP.
|
|
//
|
|
|
|
Status = RdrFscRead(CanFsdWait(Irp), TRUE, DeviceObject, Irp);
|
|
|
|
} except (RdrExceptionFilter(GetExceptionInformation(), &Status)) {
|
|
Status = RdrProcessException( Irp, Status );
|
|
}
|
|
|
|
FsRtlExitFileSystem();
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
RdrFspRead (
|
|
IN PFS_DEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine processes the NtRead request in the redirector FSP.
|
|
|
|
Arguments:
|
|
|
|
DriverObject - Supplies a pointer to the redirector driver object.
|
|
Irp - Supplies a pointer to the IRP to be processed.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - The FSD status for this Irp.
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
#ifndef PAGING_OVER_THE_NET
|
|
PAGED_CODE();
|
|
#endif
|
|
|
|
return RdrFscRead(TRUE, FALSE, DeviceObject, Irp);
|
|
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
RdrFscRead (
|
|
IN BOOLEAN Wait,
|
|
IN BOOLEAN InFsd,
|
|
IN PFS_DEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine processes the NtRead request in either the FSP or the FSD.
|
|
|
|
Arguments:
|
|
|
|
Wait - True iff FSD can wait for IRP to complete.
|
|
DriverObject - Supplies a pointer to the redirector driver object.
|
|
Irp - Supplies a pointer to the IRP to be processed.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - The FSD status for this Irp.
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
PFILE_OBJECT FileObject = IrpSp->FileObject;
|
|
PICB Icb = FileObject->FsContext2;
|
|
PSECURITY_ENTRY Se = Icb->Se;
|
|
ULONG Length = IrpSp->Parameters.Read.Length;
|
|
LARGE_INTEGER ByteOffset = IrpSp->Parameters.Read.ByteOffset;
|
|
ULONG TotalDataRead = 0;
|
|
LARGE_INTEGER IOOffset;
|
|
LARGE_INTEGER FileSize;
|
|
LARGE_INTEGER ValidDataLength;
|
|
NTSTATUS Status;
|
|
PVOID BufferAddress; // Mapped buffer address for reads.
|
|
PLCB Lcb;
|
|
BOOLEAN BufferMapped = FALSE;
|
|
BOOLEAN FcbLocked = FALSE;
|
|
BOOLEAN PagingIoLocked = FALSE;
|
|
BOOLEAN PostToFsp = FALSE;
|
|
BOOLEAN ReadSyncSet = FALSE; // Was pipe read synchronization locked?
|
|
BOOLEAN NonCachedIo = BooleanFlagOn(Irp->Flags, IRP_NOCACHE);
|
|
BOOLEAN PagingIo = BooleanFlagOn(Irp->Flags, IRP_PAGING_IO);
|
|
BOOLEAN UseRawIo = TRUE; // True if we should use raw I/O
|
|
BOOLEAN UseRawIoOnPipe = TRUE; // True if we should use raw I/O to complete pipe read
|
|
|
|
ULONG RawReadLength = 0xffff;
|
|
|
|
#ifndef PAGING_OVER_THE_NET
|
|
PAGED_CODE();
|
|
#endif
|
|
|
|
ASSERT(Icb->Signature == STRUCTURE_SIGNATURE_ICB);
|
|
|
|
ASSERT(Se->Signature == STRUCTURE_SIGNATURE_SECURITYENTRY);
|
|
|
|
dprintf(DPRT_READWRITE, ("NtReadFile...\n"));
|
|
dprintf(DPRT_READWRITE, ("File %wZ, Read %ld bytes at %lx%lx\n",
|
|
&Icb->Fcb->FileName, Length,
|
|
IrpSp->Parameters.Read.ByteOffset.HighPart,
|
|
IrpSp->Parameters.Read.ByteOffset.LowPart));
|
|
|
|
|
|
//
|
|
// Compute the starting offset of the I/O specified as a 32 bit number.
|
|
//
|
|
|
|
ASSERT ((IrpSp->Parameters.Read.ByteOffset.HighPart==0) ||
|
|
(Icb->Fcb->Connection->Server->Capabilities & DF_LARGE_FILES) ||
|
|
(Icb->Type == NamedPipe) ||
|
|
(Icb->NonPagedFcb->FileType == FileTypePrinter) ||
|
|
(Icb->NonPagedFcb->FileType == FileTypeCommDevice) );
|
|
|
|
try {
|
|
|
|
if (Icb->Type == DiskFile) {
|
|
RdrWaitForAndXBehindOperation(&Icb->u.f.AndXBehind);
|
|
}
|
|
|
|
|
|
#ifdef PAGING_OVER_THE_NET
|
|
//
|
|
// If this I/O is to a paging file, then take our special paging read
|
|
// code path.
|
|
//
|
|
|
|
if (Icb->Fcb->Flags & FCB_PAGING_FILE) {
|
|
KIRQL OldIrql;
|
|
|
|
ASSERT (PagingIo);
|
|
|
|
ASSERT (Irp->MdlAddress);
|
|
|
|
ASSERT (Wait);
|
|
|
|
IOOffset = IrpSp->Parameters.Read.ByteOffset ;
|
|
|
|
LOCK_FILE_SIZES(Icb->Fcb, OldIrql);
|
|
|
|
FileSize = Icb->Fcb->Header.FileSize;
|
|
|
|
UNLOCK_FILE_SIZES(Icb->Fcb, OldIrql);
|
|
|
|
try_return(Status = RdrPagingRead(Irp,
|
|
Icb,
|
|
Irp->MdlAddress,
|
|
&IOOffset,
|
|
Length,
|
|
&TotalDataRead
|
|
));
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// If this is a noncached transfer and is not a paging I/O, and
|
|
// the file has a data section, then we will do a flush here
|
|
// to avoid stale data problems. Note that we must flush before
|
|
// acquiring the Fcb shared since the write may try to acquire
|
|
// it exclusive.
|
|
//
|
|
|
|
if (!PagingIo && NonCachedIo
|
|
|
|
&&
|
|
|
|
FileObject->SectionObjectPointer->DataSectionObject) {
|
|
|
|
//RdrLog(( "ccflush4", &Icb->Fcb->FileName, 2, ByteOffset.LowPart, Length ));
|
|
CcFlushCache( FileObject->SectionObjectPointer,
|
|
&ByteOffset,
|
|
Length,
|
|
&Irp->IoStatus );
|
|
|
|
if (!NT_SUCCESS( Irp->IoStatus.Status)) {
|
|
|
|
try_return( Status = Irp->IoStatus.Status );
|
|
}
|
|
|
|
//
|
|
// Serialize behind paging I/O to ensure flush is done.
|
|
//
|
|
|
|
ExAcquireResourceExclusive(Icb->Fcb->Header.PagingIoResource, TRUE);
|
|
ExReleaseResource(Icb->Fcb->Header.PagingIoResource);
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// In order to prevent corruption on multi-threaded multi-block
|
|
// message mode pipe reads, we acquire the file lock exclusive
|
|
// to prevent other threads in this process from reading from the
|
|
// pipe while this read is progressing.
|
|
//
|
|
|
|
if ((Icb->Type == NamedPipe) &&
|
|
(Icb->NonPagedFcb->FileType == FileTypeMessageModePipe) ||
|
|
((Icb->NonPagedFcb->FileType == FileTypeByteModePipe) &&
|
|
!(Icb->u.p.PipeState & SMB_PIPE_NOWAIT))) {
|
|
|
|
//
|
|
// Acquire the synchronization event that will prevent other
|
|
// threads from coming in and reading from this file while the
|
|
// message pipe read is continuing.
|
|
//
|
|
// This is necessary because we will release the FCB lock while
|
|
// actually performing the I/O to allow open (and other) requests
|
|
// to continue on this file while the I/O is in progress.
|
|
//
|
|
|
|
dprintf(DPRT_READWRITE, ("Message pipe read: Icb: %lx, Fcb: %lx, Waiting...\n", Icb, Icb->Fcb));
|
|
|
|
Status = KeWaitForSingleObject(&Icb->u.p.MessagePipeReadSync,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
(Wait ? NULL : &RdrZero));
|
|
if (Status == STATUS_TIMEOUT) {
|
|
dprintf(DPRT_READWRITE, ("Timed Out: Icb: %lx\n", Icb));
|
|
PostToFsp = TRUE;
|
|
try_return(Status = STATUS_PENDING);
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
try_return(Status);
|
|
}
|
|
|
|
dprintf(DPRT_READWRITE, ("Succeeded: Icb: %lx\n", Icb));
|
|
|
|
ReadSyncSet = TRUE;
|
|
|
|
Status = RdrCheckCanceledIrp(Irp);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
try_return(Status);
|
|
}
|
|
|
|
}
|
|
|
|
if ( PagingIo ) {
|
|
if (!ExAcquireResourceShared(Icb->Fcb->Header.PagingIoResource, Wait)) {
|
|
PostToFsp = TRUE;
|
|
try_return(Status = STATUS_PENDING);
|
|
}
|
|
|
|
PagingIoLocked = TRUE;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Acquire a shared lock to the file - Prevent delete operations on the
|
|
// file.
|
|
//
|
|
|
|
if (!RdrAcquireFcbLock(Icb->Fcb, SharedLock, Wait)) {
|
|
PostToFsp = TRUE;
|
|
try_return(Status = STATUS_PENDING);
|
|
}
|
|
|
|
FcbLocked = TRUE;
|
|
}
|
|
|
|
if ( !FlagOn(Irp->Flags, IRP_PAGING_IO) ) {
|
|
if (!NT_SUCCESS(Status = RdrIsOperationValid(Icb, IRP_MJ_READ, FileObject))) {
|
|
try_return(Status);
|
|
}
|
|
}
|
|
|
|
RdrQueryFileSizes(Icb->Fcb, &FileSize, &ValidDataLength, NULL);
|
|
|
|
//
|
|
// Statistics....
|
|
//
|
|
|
|
if ( PagingIo ) {
|
|
ExInterlockedAddLargeStatistic(
|
|
&RdrStatistics.PagingReadBytesRequested,
|
|
Length );
|
|
} else {
|
|
ExInterlockedAddLargeStatistic(
|
|
&RdrStatistics.NonPagingReadBytesRequested,
|
|
Length );
|
|
}
|
|
|
|
//
|
|
// If this read request is not a paged read, check to make sure that
|
|
// the read region of the file is not locked.
|
|
//
|
|
if (!PagingIo
|
|
|
|
&&
|
|
|
|
(Icb->Type == DiskFile)
|
|
|
|
&&
|
|
|
|
!(FsRtlCheckLockForReadAccess( &Icb->Fcb->FileLock, Irp))) {
|
|
|
|
//RdrLog(( "readCONF", &Icb->Fcb->FileName, 4, IoGetRequestorProcess(Irp), IrpSp->FileObject,
|
|
// IrpSp->Parameters.Read.ByteOffset.LowPart,
|
|
// IrpSp->Parameters.Read.Length | (FlagOn(Irp->Flags,IRP_PAGING_IO) ? 0x80000000 : 0)));
|
|
try_return(Status = STATUS_FILE_LOCK_CONFLICT);
|
|
|
|
}
|
|
|
|
if (Icb->Type == DiskFile
|
|
|
|
&&
|
|
|
|
RdrCanFileBeBuffered(Icb)) {
|
|
|
|
//
|
|
// If this file can be cached, limit the read amount to the
|
|
// file size.
|
|
//
|
|
|
|
if (FileSize.QuadPart <= ByteOffset.QuadPart + Length) {
|
|
|
|
//
|
|
// If the I/O starts before the end of the file,
|
|
// limit the read to file size.
|
|
//
|
|
|
|
if (ByteOffset.QuadPart < FileSize.QuadPart) {
|
|
|
|
Length = (ULONG)(FileSize.QuadPart - ByteOffset.QuadPart);
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// If we this is a file that can have locks, there have been locks
|
|
// applied to this file, and if the read region is inside an LCB.
|
|
//
|
|
// If it is, then we want to return the data cached in the LCB.
|
|
//
|
|
|
|
if ((Icb->Type == DiskFile)
|
|
|
|
&&
|
|
|
|
(FileObject->LockOperation)
|
|
|
|
&&
|
|
|
|
(Lcb = RdrFindLcb(&Icb->u.f.LockHead,
|
|
ByteOffset,
|
|
Length,
|
|
IrpSp->Parameters.Read.Key)) != NULL) {
|
|
|
|
LARGE_INTEGER ReadOffsetWithinBuffer;
|
|
|
|
//
|
|
// There's an LCB describing this region of the file. This means
|
|
// that we've cached the contents of a section of the file in the
|
|
// LCB that we just returned. Satisfy the user's read request out
|
|
// of the buffer.
|
|
//
|
|
|
|
ASSERT(ByteOffset.QuadPart >= Lcb->ByteOffset.QuadPart);
|
|
|
|
ASSERT(Length <= Lcb->Length);
|
|
|
|
ReadOffsetWithinBuffer.QuadPart = ByteOffset.QuadPart - Lcb->ByteOffset.QuadPart;
|
|
|
|
ASSERT((ReadOffsetWithinBuffer.HighPart == 0) ||
|
|
(Icb->Type == NamedPipe) ||
|
|
(Icb->NonPagedFcb->FileType == FileTypePrinter) ||
|
|
(Icb->NonPagedFcb->FileType == FileTypeCommDevice) );
|
|
|
|
try {
|
|
|
|
BufferMapped = RdrMapUsersBuffer(Irp, &BufferAddress, Length);
|
|
|
|
RtlCopyMemory(BufferAddress,
|
|
&Lcb->Buffer[ReadOffsetWithinBuffer.LowPart],
|
|
Length);
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
|
|
try_return(Status = GetExceptionCode());
|
|
}
|
|
|
|
//
|
|
// The copy worked, return success to the caller.
|
|
//
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
TotalDataRead = Length;
|
|
|
|
try_return(Status);
|
|
|
|
}
|
|
|
|
|
|
if (Icb->Type == NamedPipe) {
|
|
|
|
//
|
|
// If this is a non-blocking byte mode named pipe, use the read
|
|
// ahead buffer.
|
|
//
|
|
|
|
if (( Icb->NonPagedFcb->FileType == FileTypeByteModePipe ) &&
|
|
( !(FileObject->Flags & (FO_WRITE_THROUGH | FO_NO_INTERMEDIATE_BUFFERING)) ) &&
|
|
( Icb->u.p.PipeState & SMB_PIPE_NOWAIT )){
|
|
BOOLEAN Processed;
|
|
|
|
Status = RdrNpCachedRead(
|
|
Wait,
|
|
TRUE,
|
|
DeviceObject,
|
|
Irp,
|
|
&Processed,
|
|
&TotalDataRead);
|
|
|
|
if ( Processed ) {
|
|
try_return(Status);
|
|
}
|
|
|
|
//
|
|
// else there is nothing in the Readahead buffer and the caller
|
|
// has read more than the readahead buffer size. In this case we
|
|
// use the normal read code directly into the callers buffer.
|
|
//
|
|
|
|
} else if (( Icb->NonPagedFcb->FileType == FileTypeMessageModePipe ) &&
|
|
( Icb->u.p.PipeState & SMB_PIPE_NOWAIT ) &&
|
|
( RdrBackOff ( &Icb->u.p.BackOff ) ) ) {
|
|
|
|
//
|
|
// The caller is flooding the network with this
|
|
// request because the remote application has no data in the
|
|
// pipe. Respond directly to the caller that there is no data.
|
|
//
|
|
|
|
TotalDataRead = 0;
|
|
|
|
try_return(Status = STATUS_SUCCESS);
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we have this file opened exclusivly, and the read is for data
|
|
// past the end of the file, we can return STATUS_END_OF_FILE right
|
|
// now.
|
|
//
|
|
|
|
if (Icb->Type == DiskFile &&
|
|
RdrCanFileBeBuffered(Icb)) {
|
|
|
|
if (ByteOffset.QuadPart >= FileSize.QuadPart) {
|
|
|
|
try_return(Status = STATUS_END_OF_FILE);
|
|
|
|
}
|
|
}
|
|
|
|
if( FileObject->PrivateCacheMap != NULL &&
|
|
Icb->Fcb->HaveSetCacheReadAhead == FALSE ) {
|
|
|
|
if( ByteOffset.QuadPart >= PAGE_SIZE ) {
|
|
|
|
//
|
|
// We haven't set readahead and we're on the second page:
|
|
// set the readahead right now.
|
|
//
|
|
|
|
CcSetAdditionalCacheAttributes( FileObject, FALSE, FALSE );
|
|
Icb->Fcb->HaveSetCacheReadAhead = TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If this request can be cached, the file object is not in write through
|
|
// mode, try to cache the read operation.
|
|
//
|
|
|
|
if ((Icb->Type == DiskFile)
|
|
|
|
&&
|
|
|
|
RdrData.UtilizeNtCaching
|
|
|
|
&&
|
|
|
|
!NonCachedIo
|
|
|
|
&&
|
|
|
|
((FileObject->Flags & FO_WRITE_THROUGH) == 0)
|
|
|
|
&&
|
|
|
|
RdrCanFileBeBuffered(Icb)) {
|
|
|
|
//
|
|
// If this is the first read/write operation to the file, we
|
|
// want to initialize the cache here. We delay initializing the
|
|
// cache until now because the user might open/close the file
|
|
// without performing any I/O.
|
|
//
|
|
|
|
if (FileObject->PrivateCacheMap == NULL) {
|
|
|
|
CC_FILE_SIZES FileSizes;
|
|
|
|
//
|
|
// The call to CcInitializeCacheMap may raise an exception.
|
|
//
|
|
|
|
dprintf(DPRT_CACHE|DPRT_READWRITE, ("Adding file %wZ (%lx) to the cache\n", &Icb->Fcb->FileName, Icb->Fcb));
|
|
dprintf(DPRT_CACHE|DPRT_READWRITE, ("File Size: %lx%lx, ValidDataLength: %lx%lx\n", FileSize.HighPart,
|
|
FileSize.LowPart,
|
|
ValidDataLength.HighPart,
|
|
ValidDataLength.LowPart));
|
|
|
|
RdrSetAllocationSizeToFileSize(Icb->Fcb, FileSize);
|
|
FileSizes =
|
|
*((PCC_FILE_SIZES)&Icb->Fcb->Header.AllocationSize);
|
|
|
|
ASSERT( !FlagOn(FileObject->Flags, FO_CLEANUP_COMPLETE) );
|
|
|
|
CcInitializeCacheMap( FileObject,
|
|
&FileSizes,
|
|
FALSE, // We're not going to pin this data.
|
|
&DeviceObject->CacheManagerCallbacks,
|
|
Icb->Fcb);
|
|
|
|
//
|
|
// Start out with read ahead disabled
|
|
//
|
|
CcSetAdditionalCacheAttributes( FileObject, TRUE, FALSE );
|
|
|
|
//
|
|
// But go ahead and set the granularity
|
|
//
|
|
CcSetReadAheadGranularity( FileObject, 32 * 1024 );
|
|
}
|
|
|
|
try {
|
|
BufferMapped = RdrMapUsersBuffer (Irp, &BufferAddress, Length);
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
try_return(Status = GetExceptionCode());
|
|
}
|
|
|
|
if (FileObject->PrivateCacheMap != NULL ) {
|
|
|
|
LARGE_INTEGER BeyondLastByte;
|
|
|
|
dprintf(DPRT_READWRITE, ("Call cache manager to read %lx bytes at %lx%lx\n",
|
|
Length, ByteOffset.HighPart, ByteOffset.LowPart));
|
|
|
|
//
|
|
// If the throughput is high enough we want to enable readahead.
|
|
// Check to see if the information we last received from the transport
|
|
// (stored in the sle) matches what we have told the cache to do.
|
|
//
|
|
|
|
if ((Icb->Fcb->Connection->Server->Reliable != Icb->u.f.CcReliable ) ||
|
|
(Icb->Fcb->Connection->Server->ReadAhead != Icb->u.f.CcReadAhead )) {
|
|
|
|
Icb->u.f.CcReadAhead = Icb->Fcb->Connection->Server->ReadAhead;
|
|
|
|
Icb->u.f.CcReliable = Icb->Fcb->Connection->Server->Reliable;
|
|
|
|
dprintf(DPRT_READWRITE, ("Set cache manager CcReadAhead %x Reliable%x\n",
|
|
Icb->u.f.CcReadAhead, Icb->u.f.CcReliable));
|
|
|
|
CcSetAdditionalCacheAttributes(FileObject,
|
|
(BOOLEAN)(Icb->u.f.CcReadAhead == FALSE), // DisableReadAhead
|
|
FALSE ); // DisableWriteBehind
|
|
}
|
|
|
|
ExInterlockedAddLargeStatistic(
|
|
&RdrStatistics.CacheReadBytesRequested,
|
|
Length );
|
|
|
|
//
|
|
// We must handle end of file ourselves, because Cc no longer
|
|
// checks.
|
|
//
|
|
|
|
BeyondLastByte.QuadPart = ByteOffset.QuadPart + Length;
|
|
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
Irp->IoStatus.Information = Length;
|
|
|
|
if (BeyondLastByte.QuadPart > FileSize.QuadPart) {
|
|
|
|
if (ByteOffset.QuadPart >= FileSize.QuadPart) {
|
|
Irp->IoStatus.Status = STATUS_END_OF_FILE;
|
|
Irp->IoStatus.Information = 0;
|
|
} else {
|
|
Irp->IoStatus.Information = (ULONG)(FileSize.QuadPart - ByteOffset.QuadPart);
|
|
}
|
|
}
|
|
|
|
if ((Irp->IoStatus.Information != 0) &&
|
|
!CcCopyRead(FileObject,
|
|
&ByteOffset,
|
|
Irp->IoStatus.Information,
|
|
Wait,
|
|
BufferAddress,
|
|
&Irp->IoStatus)) {
|
|
|
|
//
|
|
// The copy failed because we couldn't block the thread
|
|
// to perform the I/O. Post the request to the FSP and
|
|
// unwind this call.
|
|
//
|
|
|
|
PostToFsp = TRUE;
|
|
|
|
try_return(Status = STATUS_PENDING);
|
|
|
|
} else {
|
|
|
|
if (NT_SUCCESS(Irp->IoStatus.Status)) {
|
|
|
|
dprintf(DPRT_READWRITE, ("Read %lx bytes\n", Irp->IoStatus.Information));
|
|
|
|
//
|
|
// We have successfully read in the data out of the
|
|
// cache.
|
|
//
|
|
// Update some local variables to aid the try/finally
|
|
// code
|
|
//
|
|
|
|
TotalDataRead = Irp->IoStatus.Information;
|
|
IOOffset.QuadPart = IrpSp->Parameters.Read.ByteOffset.QuadPart + TotalDataRead;
|
|
}
|
|
|
|
try_return(Status = Irp->IoStatus.Status);
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
IOOffset = IrpSp->Parameters.Read.ByteOffset ;
|
|
|
|
//
|
|
// If we cannot tie up the current thread, post the request to the
|
|
// FSP.
|
|
//
|
|
// At this point, we are commited to hitting the network for this
|
|
// request, so we will be tying up the thread.
|
|
//
|
|
|
|
if (!Wait) {
|
|
|
|
ASSERT(InFsd);
|
|
|
|
PostToFsp = TRUE;
|
|
|
|
try_return(Status = STATUS_PENDING);
|
|
|
|
}
|
|
|
|
dprintf(DPRT_READWRITE, ("Actual I/O Offset is %lx%lx\n", IOOffset.HighPart, IOOffset.LowPart));
|
|
|
|
ExInterlockedAddLargeStatistic(
|
|
&RdrStatistics.NetworkReadBytesRequested,
|
|
Length );
|
|
|
|
//
|
|
// Ignore the size of the SMB_HEADER and RESP_READ to keep it simple.
|
|
// Small reads are less than 1/4 the servers negotiated buffer size
|
|
// Small reads are larger than twice the servers negotiated buffer size
|
|
//
|
|
|
|
if ( Length < (Icb->Fcb->Connection->Server->BufferSize / 4) ) {
|
|
RdrStatistics.SmallReadSmbs += 1;
|
|
} else {
|
|
if ( Length > (Icb->Fcb->Connection->Server->BufferSize * 2) ) {
|
|
RdrStatistics.LargeReadSmbs += 1;
|
|
}
|
|
}
|
|
|
|
if ( IOOffset.QuadPart != Icb->u.f.NextReadOffset.QuadPart ) {
|
|
RdrStatistics.RandomReadOperations += 1;
|
|
}
|
|
|
|
//
|
|
// ValidDataLength check.
|
|
//
|
|
// If the file in question is a disk file, and it is currently cached,
|
|
// and the read offset is greater than valid data length, then
|
|
// return 0s to the application.
|
|
//
|
|
|
|
if ((Icb->Type == DiskFile)
|
|
|
|
&&
|
|
|
|
CcIsFileCached(FileObject)
|
|
|
|
&&
|
|
|
|
ByteOffset.QuadPart >= ValidDataLength.QuadPart) {
|
|
|
|
try {
|
|
|
|
//
|
|
// Calculate the number of zeroes that are needed.
|
|
//
|
|
|
|
//
|
|
// If ByteOffset is beyond FileSize, there is nothing to read.
|
|
//
|
|
|
|
if (ByteOffset.QuadPart >= FileSize.QuadPart) {
|
|
|
|
Length = 0;
|
|
|
|
} else {
|
|
|
|
//
|
|
// There is at least one byte available. Truncate
|
|
// the transfer length if it goes beyond EOF.
|
|
//
|
|
|
|
LARGE_INTEGER TransferEnd;
|
|
|
|
//
|
|
// TransferEnd is the first byte AFTER the requested data.
|
|
//
|
|
|
|
TransferEnd.QuadPart = ByteOffset.QuadPart + Length;
|
|
|
|
if (TransferEnd.QuadPart > FileSize.QuadPart) {
|
|
|
|
LARGE_INTEGER LengthRemaining;
|
|
|
|
LengthRemaining.QuadPart = FileSize.QuadPart - ByteOffset.QuadPart;
|
|
ASSERT (LengthRemaining.HighPart == 0);
|
|
Length = LengthRemaining.LowPart;
|
|
|
|
}
|
|
|
|
BufferMapped = RdrMapUsersBuffer(Irp, &BufferAddress, Length);
|
|
|
|
RtlZeroMemory(BufferAddress, Length);
|
|
|
|
}
|
|
|
|
Irp->IoStatus.Information = Length;
|
|
|
|
//
|
|
// Indicate we read all the data.
|
|
//
|
|
|
|
TotalDataRead = Length;
|
|
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
try_return(Status = GetExceptionCode());
|
|
}
|
|
|
|
try_return(Status = STATUS_SUCCESS);
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// If this request won't fit into a single request, break it up
|
|
// into some more reasonable amount.
|
|
//
|
|
if (Length > 0xffff) {
|
|
RawReadLength = 0xFFFF;
|
|
}
|
|
|
|
//
|
|
// Check the static fields that determine if we are to use raw I/O
|
|
// outside the main read loop. These tests are all loop invarient,
|
|
// since they will not change while the loop is executing.
|
|
//
|
|
|
|
if (!RdrData.UseRawRead) {
|
|
UseRawIo = FALSE;
|
|
UseRawIoOnPipe = FALSE;
|
|
}
|
|
|
|
//
|
|
// If the server supports either variety of raw I/O, we can use
|
|
// raw I/O for this read.
|
|
//
|
|
|
|
if ((Icb->Fcb->Connection->Server->Capabilities & (DF_OLDRAWIO | DF_NEWRAWIO)) == 0) {
|
|
UseRawIo = FALSE;
|
|
UseRawIoOnPipe = FALSE;
|
|
}
|
|
|
|
//
|
|
// Don't use raw read on comm devices, they are blocking.
|
|
//
|
|
|
|
if (Icb->Type == Com) {
|
|
UseRawIo = FALSE;
|
|
UseRawIoOnPipe = FALSE;
|
|
}
|
|
|
|
//
|
|
// If this is a named pipe, and it is in blocking mode, don't use
|
|
// raw read on it.
|
|
//
|
|
|
|
if ((Icb->Type == NamedPipe) &&
|
|
((Icb->u.p.PipeState & SMB_PIPE_NOWAIT) == 0)) {
|
|
UseRawIo = FALSE;
|
|
}
|
|
|
|
//
|
|
// Just because a server's protocol level supports raw I/O does
|
|
// not necessarily mean that it can support raw I/O. Check to
|
|
// see if this server actually supports raw I/O.
|
|
//
|
|
|
|
if (!Icb->Fcb->Connection->Server->SupportsRawRead) {
|
|
UseRawIo = FALSE;
|
|
UseRawIoOnPipe = FALSE;
|
|
}
|
|
|
|
if( Icb->Type == DiskFile &&
|
|
(Icb->Fcb->Connection->Server->Capabilities & DF_LARGE_READX) ) {
|
|
|
|
UseRawIo = FALSE;
|
|
}
|
|
|
|
while (Length > 0) {
|
|
ULONG AmountActuallyRead = 0; // Amount actually read from file.
|
|
BOOLEAN AllReadDataReturned = FALSE;
|
|
ULONG BytesRemainingToBeRead = 0;
|
|
|
|
if (ReadSyncSet) {
|
|
ASSERT (Icb->Type == NamedPipe);
|
|
|
|
ASSERT ( !PagingIo );
|
|
|
|
ASSERT (FcbLocked);
|
|
|
|
RdrReleaseFcbLock(Icb->Fcb);
|
|
|
|
FcbLocked = FALSE;
|
|
#if DBG
|
|
ASSERT (!ExIsResourceAcquiredExclusive(Icb->Fcb->Header.Resource));
|
|
#endif
|
|
|
|
}
|
|
|
|
if (UseRawIo) {
|
|
//
|
|
// We want to limit the amount of data read in to the
|
|
// minimum of the requested length and the server's negotiated
|
|
// buffer size (adjusted by the size of an SMB header).
|
|
//
|
|
|
|
Status = RawRead(Irp, Icb, IOOffset, MIN(RawReadLength, Length),
|
|
TotalDataRead,
|
|
&AllReadDataReturned,
|
|
&AmountActuallyRead);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
//
|
|
// If there was a network error on the read, return it.
|
|
//
|
|
|
|
try_return(Status);
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we were unable to read any data using read raw, try reading
|
|
// the data using core read. There are no errors for raw read,
|
|
// so the only way to know the true error is to return 0 to the
|
|
// number of bytes of data read.
|
|
//
|
|
|
|
if (AmountActuallyRead == 0) {
|
|
|
|
if (Icb->Fcb->Connection->Server->Capabilities & DF_LANMAN10) {
|
|
|
|
//
|
|
// Use Lan Manager SMB protocols to read the data from the file.
|
|
//
|
|
|
|
Status = ReadAndX(Irp, Icb, MIN(Length, 0xffff), IOOffset,
|
|
TotalDataRead,
|
|
&AllReadDataReturned,
|
|
&AmountActuallyRead,
|
|
&BytesRemainingToBeRead);
|
|
|
|
if ((Status == STATUS_BUFFER_OVERFLOW) &&
|
|
(Icb->Type == NamedPipe) &&
|
|
AllReadDataReturned &&
|
|
(Icb->NonPagedFcb->FileType == FileTypeMessageModePipe) &&
|
|
(BytesRemainingToBeRead != 0) &&
|
|
UseRawIoOnPipe) {
|
|
|
|
//
|
|
// If there is enough data left in this message
|
|
// to justify a raw read, then try a raw read and
|
|
// see what happens.
|
|
//
|
|
|
|
dprintf(DPRT_READWRITE, ("Pipe read, %ld bytes remaining\n", BytesRemainingToBeRead));
|
|
|
|
if (BytesRemainingToBeRead >= Icb->Fcb->Connection->Server->BufferSize * RAW_THRESHOLD) {
|
|
|
|
dprintf(DPRT_READWRITE, ("Pipe read. Try to read %ld bytes using raw\n", BytesRemainingToBeRead));
|
|
|
|
//
|
|
// We update length to match the # of bytes
|
|
// remaining to be read, because the application
|
|
// may have actually requested MORE data than we
|
|
// have to give it...
|
|
//
|
|
|
|
Length = BytesRemainingToBeRead;
|
|
|
|
TotalDataRead += AmountActuallyRead;
|
|
|
|
IOOffset.QuadPart = IOOffset.QuadPart + AmountActuallyRead;
|
|
|
|
Status = RawRead(Irp, Icb, IOOffset,
|
|
MIN(RawReadLength, BytesRemainingToBeRead),
|
|
TotalDataRead,
|
|
&AllReadDataReturned,
|
|
&AmountActuallyRead);
|
|
|
|
//
|
|
// If we can't do this I/O using raw, then
|
|
// AmountActuallyRead will be equal to 0, and
|
|
// AllReadDataReturned will be false.
|
|
//
|
|
|
|
}
|
|
}
|
|
} else {
|
|
|
|
//
|
|
// Use core read SMB protocols to read the data from the file.
|
|
//
|
|
|
|
Status = CoreRead(Irp, Icb, MIN(Length, 0xffff), IOOffset,
|
|
TotalDataRead,
|
|
&AllReadDataReturned,
|
|
&AmountActuallyRead);
|
|
}
|
|
|
|
if (NT_ERROR(Status)) {
|
|
try_return(Status);
|
|
}
|
|
|
|
}
|
|
|
|
if (ReadSyncSet) {
|
|
|
|
ASSERT ( !PagingIo );
|
|
|
|
RdrAcquireFcbLock(Icb->Fcb, SharedLock, TRUE);
|
|
|
|
if (Icb->Type == DiskFile) {
|
|
RdrQueryFileSizes(Icb->Fcb, &FileSize, NULL, NULL);
|
|
}
|
|
|
|
FcbLocked = TRUE;
|
|
}
|
|
|
|
//
|
|
// Account for the amount of data read. We update:
|
|
//
|
|
// 1) The requested length
|
|
// 2) The running count of the total amount of data read.
|
|
// 3) The I/O transfer address.
|
|
//
|
|
|
|
Length -= AmountActuallyRead;
|
|
|
|
TotalDataRead += AmountActuallyRead;
|
|
|
|
IOOffset.QuadPart += AmountActuallyRead;
|
|
|
|
//
|
|
// If the remote server ever returned less bytes than those that
|
|
// we requested, then that's all we're going to get, so we should
|
|
// return right now.
|
|
//
|
|
|
|
if (!AllReadDataReturned) {
|
|
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
try_exit: {
|
|
|
|
//
|
|
// This code is called on the successful (non excepted) return
|
|
// from RdrFscRead.
|
|
//
|
|
|
|
if (PostToFsp) {
|
|
|
|
Status = RdrLockUsersBuffer(Irp, IoWriteAccess, IrpSp->Parameters.Read.Length);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
Status = STATUS_PENDING;
|
|
RdrFsdPostToFsp(DeviceObject, Irp);
|
|
} else {
|
|
PostToFsp = FALSE;
|
|
}
|
|
|
|
} else if (!NT_ERROR(Status)) {
|
|
|
|
KeQuerySystemTime(&Icb->Fcb->LastAccessTime);
|
|
|
|
//
|
|
// If we got 0 bytes from the remote server, then we can assume
|
|
// that we're at end of file, so return the appropriate error.
|
|
//
|
|
|
|
if (TotalDataRead == 0) {
|
|
switch (Icb->Type) {
|
|
|
|
case NamedPipe:
|
|
|
|
//
|
|
// If we got 0 bytes transferred on a nonblocking mode
|
|
// pipe then tell the backoff package so that we avoid flooding
|
|
// the network with requests that get no data returned.
|
|
//
|
|
|
|
if ( Icb->u.p.PipeState & SMB_PIPE_NOWAIT ) {
|
|
RdrBackPackFailure( &Icb->u.p.BackOff );
|
|
}
|
|
|
|
if ( Icb->u.p.PipeState & SMB_PIPE_READMODE_MESSAGE) {
|
|
Status = STATUS_PIPE_EMPTY;
|
|
} else {
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
Status = STATUS_END_OF_FILE;
|
|
}
|
|
|
|
} else {
|
|
if ((Icb->Type == NamedPipe) || (Icb->Type == Com)) {
|
|
|
|
//
|
|
// If we have been backing off the user then receiving
|
|
// data swiches the backoff delta back to zero
|
|
//
|
|
|
|
RdrBackPackSuccess( &Icb->u.p.BackOff );
|
|
|
|
} else {
|
|
|
|
//
|
|
// If this is a disk file, we read data, and the read offset
|
|
// is beyond the nominal end of file, update the
|
|
// file size to indicate the file just got a bit longer.
|
|
//
|
|
//
|
|
// We perform this test on all files, regardless of
|
|
// whether or not they can be buffered. If the file is
|
|
// opened exclusively, this test will never succeed, and
|
|
// if it is not opened exclusively, we only use this
|
|
// information to determine if we want to use lock&read.
|
|
//
|
|
|
|
if (IOOffset.QuadPart > FileSize.QuadPart) {
|
|
|
|
ASSERT(!RdrCanFileBeBuffered(Icb));
|
|
|
|
//
|
|
// If I/O is not for paging I/O, re-acquire the FCB
|
|
// lock.
|
|
//
|
|
|
|
if ( !PagingIo ) {
|
|
|
|
//
|
|
// Fcb->Header.FileSize is protected by the FCB
|
|
// resource, so we have to release the FCB and
|
|
// re-acquire it. Once we re-acquire it, we have
|
|
// to check again to see if we really have to
|
|
// update the length.
|
|
//
|
|
|
|
ASSERT (FcbLocked);
|
|
|
|
RdrReleaseFcbLock(Icb->Fcb);
|
|
|
|
FcbLocked = FALSE;
|
|
|
|
#if DBG
|
|
if ( !PagingIo ) {
|
|
ASSERT (!ExIsResourceAcquiredExclusive(Icb->Fcb->Header.Resource));
|
|
}
|
|
#endif
|
|
|
|
RdrAcquireFcbLock(Icb->Fcb, ExclusiveLock, TRUE);
|
|
|
|
FcbLocked = TRUE;
|
|
} else {
|
|
ASSERT (PagingIoLocked || Icb->NonPagedFcb->Flags & FCB_PAGING_FILE);
|
|
}
|
|
|
|
//
|
|
// We now own the FCB exclusive, perform the check
|
|
// again in case another thread came in and changed
|
|
// it before we were able to re-acquire the FCB.
|
|
//
|
|
|
|
if (IOOffset.QuadPart > FileSize.QuadPart) {
|
|
|
|
RdrSetFileSize(Icb->Fcb, IOOffset);
|
|
|
|
if (FileObject->PrivateCacheMap != NULL) {
|
|
CC_FILE_SIZES FileSizes = *((PCC_FILE_SIZES)&Icb->Fcb->Header.AllocationSize);
|
|
|
|
//
|
|
// Tell the cache manager about this just in case.
|
|
//
|
|
|
|
CcSetFileSizes( FileObject, &FileSizes );
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
// For disk files record where the next non-random Read would start
|
|
if (Icb->Type == DiskFile) {
|
|
RdrUpdateNextReadOffset(Icb, IOOffset);
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Set the total amount of data transfered before returning.
|
|
//
|
|
|
|
Irp->IoStatus.Information = TotalDataRead;
|
|
|
|
//
|
|
// Update the current byte offset in the file if it is a synchronous
|
|
// file.
|
|
//
|
|
|
|
if ( FlagOn(FileObject->Flags, FO_SYNCHRONOUS_IO) &&
|
|
!PagingIo ) {
|
|
|
|
FileObject->CurrentByteOffset.QuadPart = ByteOffset.QuadPart + TotalDataRead;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// The finally clause of the read logic is called whenever a read
|
|
// request completes. It unlocks and unmaps whatever data is appropriate.
|
|
//
|
|
|
|
} finally {
|
|
|
|
if (BufferMapped) {
|
|
RdrUnMapUsersBuffer(Irp, BufferAddress);
|
|
}
|
|
|
|
//
|
|
// The read operation has completed, it's ok to release the file's lock
|
|
//
|
|
|
|
if (PagingIoLocked) {
|
|
ExReleaseResource(Icb->Fcb->Header.PagingIoResource);
|
|
|
|
PagingIoLocked = FALSE;
|
|
}
|
|
|
|
if (FcbLocked) {
|
|
RdrReleaseFcbLock(Icb->Fcb);
|
|
|
|
FcbLocked = FALSE;
|
|
#if DBG
|
|
if ( !PagingIo ) {
|
|
ASSERT (!ExIsResourceAcquiredExclusive(Icb->Fcb->Header.Resource));
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
//
|
|
// Release the read synchronization event, this read is now done.
|
|
//
|
|
|
|
if (ReadSyncSet) {
|
|
dprintf(DPRT_READWRITE, ("Release Read sync: %lx\n", Icb));
|
|
KeSetEvent(&Icb->u.p.MessagePipeReadSync, IO_NETWORK_INCREMENT, FALSE);
|
|
}
|
|
|
|
dprintf(DPRT_READWRITE, ("Read complete, returning %lx bytes read, Status=%X\n", Irp->IoStatus.Information, Status));
|
|
|
|
if (!PostToFsp && !AbnormalTermination()) {
|
|
|
|
#if DBG
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
ASSERT((Irp->IoStatus.Information != 0) ||
|
|
(Icb->Type != DiskFile));
|
|
|
|
|
|
}
|
|
#endif
|
|
//
|
|
// If this is a paging read, we need to flush the MDL
|
|
// since on some systems the I-cache and D-cache
|
|
// are not synchronized.
|
|
//
|
|
|
|
if ( PagingIo ) {
|
|
KeFlushIoBuffers(Irp->MdlAddress, TRUE, FALSE);
|
|
}
|
|
|
|
RdrCompleteRequest(Irp, Status);
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
|
|
UNREFERENCED_PARAMETER(DeviceObject);
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
RdrQueryFileSizes(
|
|
IN PFCB Fcb,
|
|
OUT PLARGE_INTEGER FileSize,
|
|
OUT PLARGE_INTEGER ValidDataLength,
|
|
OUT PLARGE_INTEGER AllocationSize
|
|
)
|
|
{
|
|
// KIRQL OldIrql;
|
|
|
|
PAGED_CODE();
|
|
|
|
LOCK_FILE_SIZES(Fcb, OldIrql);
|
|
|
|
*FileSize = Fcb->Header.FileSize;
|
|
|
|
if (ARGUMENT_PRESENT(ValidDataLength)) {
|
|
*ValidDataLength = Fcb->Header.ValidDataLength;
|
|
}
|
|
|
|
if (ARGUMENT_PRESENT(AllocationSize)) {
|
|
*AllocationSize = Fcb->Header.AllocationSize;
|
|
}
|
|
|
|
UNLOCK_FILE_SIZES(Fcb, OldIrql);
|
|
|
|
}
|
|
|
|
LARGE_INTEGER
|
|
RdrQueryFileSize(
|
|
IN PFCB Fcb
|
|
)
|
|
{
|
|
// KIRQL OldIrql;
|
|
LARGE_INTEGER FileSize;
|
|
|
|
PAGED_CODE();
|
|
|
|
LOCK_FILE_SIZES(Fcb, OldIrql);
|
|
|
|
FileSize = Fcb->Header.FileSize;
|
|
|
|
UNLOCK_FILE_SIZES(Fcb, OldIrql);
|
|
|
|
return FileSize;
|
|
|
|
}
|
|
|
|
VOID
|
|
RdrUpdateNextReadOffset(
|
|
IN PICB Icb,
|
|
IN LARGE_INTEGER IOOffset
|
|
)
|
|
|
|
{
|
|
KIRQL OldIrql;
|
|
|
|
DISCARDABLE_CODE(RdrFileDiscardableSection);
|
|
|
|
ACQUIRE_SPIN_LOCK(&RdrStatisticsSpinLock, &OldIrql);
|
|
Icb->u.f.NextReadOffset = IOOffset;
|
|
RELEASE_SPIN_LOCK(&RdrStatisticsSpinLock, OldIrql);
|
|
}
|
|
|
|
#ifdef PAGING_OVER_THE_NET
|
|
NTSTATUS
|
|
RdrPagingRead(
|
|
IN PIRP Irp,
|
|
IN PICB Icb,
|
|
IN PMDL MdlAddress,
|
|
IN PLARGE_INTEGER ByteOffset,
|
|
IN ULONG Length,
|
|
IN PULONG TotalDataRead
|
|
)
|
|
{
|
|
BOOLEAN allReadDataReturned;
|
|
ULONG amountActuallyRead;
|
|
NTSTATUS status;
|
|
|
|
while (Length) {
|
|
status = ReadAndX(Irp, Icb, MIN(Length, 0xffff),
|
|
*ByteOffset,
|
|
*TotalDataRead,
|
|
&allReadDataReturned,
|
|
&amountActuallyRead);
|
|
if (NT_ERROR(status)) {
|
|
return status;
|
|
}
|
|
|
|
Length -= amountActuallyRead;
|
|
|
|
*TotalDataRead += amountActuallyRead;
|
|
|
|
*ByteOffset.QuadPart += amountActuallyRead;
|
|
|
|
if (!allReadDataReturned) {
|
|
return status;
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
#endif
|
|
|
|
NTSTATUS
|
|
RdrCheckCanceledIrp(
|
|
IN PIRP Irp
|
|
)
|
|
|
|
{
|
|
DISCARDABLE_CODE(RdrFileDiscardableSection);
|
|
//
|
|
// If this IRP was canceled between when we attempted to acquire
|
|
// the pipe synchronization event and the time we actually
|
|
// acquired the pipe synchronization event, we want to return to
|
|
// the caller immediately.
|
|
//
|
|
// This can happen if we have multiple write or read operations
|
|
// outstanding on a pipe and one of the threads waiting on the
|
|
// pipe is terminated.
|
|
//
|
|
|
|
IoAcquireCancelSpinLock(&Irp->CancelIrql);
|
|
|
|
if (Irp->Cancel) {
|
|
IoReleaseCancelSpinLock(Irp->CancelIrql);
|
|
|
|
return STATUS_CANCELLED;
|
|
} else {
|
|
IoReleaseCancelSpinLock(Irp->CancelIrql);
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
DBGSTATIC
|
|
NTSTATUS
|
|
CoreRead (
|
|
IN PIRP Irp,
|
|
IN PICB Icb,
|
|
IN ULONG Length,
|
|
IN LARGE_INTEGER ReadOffset,
|
|
IN ULONG TotalReadSoFar,
|
|
OUT PBOOLEAN AllDataRead,
|
|
OUT PULONG AmountActuallyRead
|
|
)
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine uses the core SMB read protocol to read from the specified
|
|
file.
|
|
|
|
|
|
Arguments:
|
|
|
|
IN PIRP Irp - Supplies an IRP to use for the raw read request.
|
|
IN PICB Icb - Supplies an ICB for the file to read.
|
|
IN LARGE_INTEGER ReadOffset - Supplies the offset to read from in the file.
|
|
IN ULONG Length - Supplies the total number of bytes to read.
|
|
IN ULONG TotalDataReadSoFar - Supplies the # of bytes read so far.
|
|
OUT PBOOLEAN AllDataRead - Returns true if all the data requested was read
|
|
OUT PULONG AmountActuallyRead - Returns the number of bytes read.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Status of read request.
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
PSMB_BUFFER SendSmbBuffer = NULL;
|
|
PSMB_BUFFER ReceiveSmbBuffer = NULL;
|
|
PSMB_HEADER Smb;
|
|
PRESP_READ ReadResponse; // Pointer to read information in SMB
|
|
PREQ_READ Read;
|
|
PMDL DataMdl; // MDL mapped into user's buffer.
|
|
NTSTATUS Status;
|
|
ULONG Flags = NT_NORMAL | NT_NORECONNECT;
|
|
ULONG SrvReadSize = Icb->Fcb->Connection->Server->BufferSize -
|
|
(sizeof(SMB_HEADER)+sizeof(RESP_READ));
|
|
USHORT AmountRequestedToRead = (USHORT )MIN(Length, SrvReadSize);
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Allocate an SMB buffer for the read operation.
|
|
//
|
|
|
|
if ((SendSmbBuffer = RdrAllocateSMBBuffer())==NULL) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto ReturnError;
|
|
}
|
|
|
|
//
|
|
// Also allocate one to hold the response SMB buffer header.
|
|
//
|
|
|
|
if ((ReceiveSmbBuffer = RdrAllocateSMBBuffer())==NULL) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto ReturnError;
|
|
}
|
|
|
|
RdrStatistics.ReadSmbs += 1;
|
|
ASSERT (AmountRequestedToRead <= 0xffff);
|
|
|
|
Smb = (PSMB_HEADER )(SendSmbBuffer->Buffer);
|
|
|
|
Smb->Command = SMB_COM_READ;
|
|
|
|
Read = (PREQ_READ )(Smb+1);
|
|
|
|
Read->WordCount = 5;
|
|
SmbPutUshort(&Read->Fid, Icb->FileId);
|
|
SmbPutUshort(&Read->Count, (USHORT )(AmountRequestedToRead & 0xffff));
|
|
SmbPutUshort(&Read->Remaining, (USHORT )MIN(0xffff, Length));
|
|
SmbPutUlong(&Read->Offset, ReadOffset.LowPart);
|
|
SmbPutUshort(&Read->ByteCount, 0);
|
|
|
|
dprintf(DPRT_READWRITE, ("Read %x bytes, %x remaining (%lx), offset %lx\n", SmbGetUshort(&Read->Count), SmbGetUshort(&Read->Remaining),
|
|
Length, ReadOffset.LowPart));
|
|
|
|
//
|
|
// Set the number of bytes to send in this request.
|
|
//
|
|
|
|
SendSmbBuffer->Mdl->ByteCount = sizeof(SMB_HEADER)+sizeof(REQ_READ);
|
|
|
|
//
|
|
// Set the size of the data to be received into the SMB buffer.
|
|
//
|
|
|
|
ReceiveSmbBuffer->Mdl->ByteCount=
|
|
sizeof(SMB_HEADER) + FIELD_OFFSET(RESP_READ, Buffer[0]);
|
|
|
|
//
|
|
// Allocate an MDL large enough to hold this piece of the
|
|
// request.
|
|
//
|
|
|
|
DataMdl = IoAllocateMdl((PCHAR )Irp->UserBuffer + TotalReadSoFar,
|
|
AmountRequestedToRead, // Length
|
|
FALSE, // Secondary Buffer
|
|
FALSE, // Charge Quota
|
|
NULL);
|
|
|
|
|
|
|
|
if (DataMdl == NULL) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto ReturnError;
|
|
}
|
|
|
|
//
|
|
// If there is no MDL for this read, probe the data MDL to lock it's
|
|
// pages down.
|
|
//
|
|
// Otherwise, use the data MDL as a partial MDL and lock the pages
|
|
// accordingly
|
|
//
|
|
|
|
if (Irp->MdlAddress == NULL) {
|
|
|
|
try {
|
|
|
|
MmProbeAndLockPages(DataMdl, Irp->RequestorMode, IoWriteAccess);
|
|
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
|
|
IoFreeMdl(DataMdl);
|
|
|
|
Status = GetExceptionCode();
|
|
goto ReturnError;
|
|
}
|
|
|
|
} else {
|
|
|
|
IoBuildPartialMdl(Irp->MdlAddress, DataMdl,
|
|
(PCHAR )Irp->UserBuffer + TotalReadSoFar,
|
|
AmountRequestedToRead);
|
|
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Now link this new MDL into the SMB buffer we allocated for
|
|
// the receive.
|
|
//
|
|
|
|
ReceiveSmbBuffer->Mdl->Next = DataMdl;
|
|
|
|
if ((Icb->Type == NamedPipe) &&
|
|
!(Icb->u.p.PipeState & SMB_PIPE_NOWAIT)) {
|
|
Flags |= NT_LONGTERM;
|
|
}
|
|
|
|
Status = RdrNetTranceive(Flags,
|
|
Irp,
|
|
Icb->Fcb->Connection,
|
|
SendSmbBuffer->Mdl,
|
|
ReceiveSmbBuffer->Mdl,
|
|
Icb->Se);
|
|
|
|
if (Irp->MdlAddress == NULL) {
|
|
MmUnlockPages(DataMdl);
|
|
}
|
|
|
|
IoFreeMdl(DataMdl);
|
|
|
|
if (!NT_ERROR(Status)) {
|
|
|
|
ReadResponse = (PRESP_READ )(((PSMB_HEADER )ReceiveSmbBuffer->Buffer)+1);
|
|
|
|
*AmountActuallyRead = SmbGetUshort(&ReadResponse->Count);
|
|
|
|
*AllDataRead = (BOOLEAN )(((USHORT)*AmountActuallyRead) == AmountRequestedToRead);
|
|
|
|
ASSERT(*AmountActuallyRead==(ULONG)(SmbGetUshort(&ReadResponse->ByteCount)-(USHORT)3));
|
|
|
|
if ( Status != STATUS_BUFFER_OVERFLOW &&
|
|
(Icb->Type == NamedPipe)) {
|
|
|
|
//
|
|
// The server did not overflow the buffer so stop submitting
|
|
// core reads to the server. This is usually only a problem
|
|
// on blocking mode pipes when the data being transferred
|
|
// matches the srvwritesize. In this case the extra read will
|
|
// block.
|
|
//
|
|
|
|
*AllDataRead = FALSE;
|
|
}
|
|
|
|
} else {
|
|
if (Status == STATUS_INVALID_HANDLE) {
|
|
RdrInvalidateFileId(Icb->NonPagedFcb, Icb->FileId);
|
|
}
|
|
}
|
|
|
|
ReturnError:
|
|
|
|
if (SendSmbBuffer!=NULL) {
|
|
RdrFreeSMBBuffer(SendSmbBuffer);
|
|
}
|
|
|
|
if (ReceiveSmbBuffer!=NULL) {
|
|
RdrFreeSMBBuffer(ReceiveSmbBuffer);
|
|
}
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
DBGSTATIC
|
|
NTSTATUS
|
|
ReadAndX (
|
|
IN PIRP Irp,
|
|
IN PICB Icb,
|
|
IN ULONG Length,
|
|
IN LARGE_INTEGER ReadOffset,
|
|
IN ULONG TotalReadSoFar,
|
|
OUT PBOOLEAN AllDataRead,
|
|
OUT PULONG AmountActuallyRead,
|
|
OUT PULONG BytesRemainingToBeRead OPTIONAL
|
|
)
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine uses the Lanman 1.0 Read&X SMB read protocol to read from the
|
|
specified file.
|
|
|
|
|
|
Arguments:
|
|
|
|
IN PIRP Irp - Supplies an IRP to use for the raw read request.
|
|
IN PICB Icb - Supplies an ICB for the file to read.
|
|
IN LARGE_INTEGER ReadOffset - Supplies the offset to read from in the file.
|
|
IN ULONG Length - Supplies the total number of bytes to read.
|
|
IN ULONG TotalDataReadSoFar - Supplies the # of bytes read so far.
|
|
OUT PBOOLEAN AllDataRead - Returns true if all the data requested was read
|
|
OUT PULONG AmountActuallyRead - Returns the number of bytes read.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Status of read request.
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOLEAN DataMdlLocked = FALSE;
|
|
PSMB_BUFFER SendSmbBuffer = NULL;
|
|
PSMB_HEADER Smb;
|
|
PRESP_READ_ANDX ReadResponse; // Pointer to read information in SMB
|
|
PREQ_READ_ANDX Read;
|
|
NTSTATUS Status;
|
|
ULONG Flags = NT_NORMAL | NT_NORECONNECT | NT_DONTSCROUNGE;
|
|
ULONG SrvReadSize;
|
|
USHORT SmallReadXSize;
|
|
|
|
BOOLEAN ConnectionObjectReferenced = FALSE;
|
|
READ_ANDX_CONTEXT Context;
|
|
USHORT AmountRequestedToRead;
|
|
PSERVERLISTENTRY Server;
|
|
BOOLEAN DoingLargeReadX;
|
|
|
|
#ifndef PAGING_OVER_THE_NET
|
|
PAGED_CODE();
|
|
#endif
|
|
|
|
Server = Icb->Fcb->Connection->Server;
|
|
|
|
|
|
//
|
|
// If the server supports large reads, and we are working with a disk file,
|
|
// then use a larger read size;
|
|
//
|
|
SmallReadXSize = (USHORT)Server->BufferSize -
|
|
(sizeof(SMB_HEADER)+sizeof(RESP_READ_ANDX)+3);
|
|
|
|
if ( (Icb->Type == DiskFile) &&
|
|
(Server->Capabilities & DF_LARGE_READX) &&
|
|
Server->BufferSize < LARGEST_READANDX ) {
|
|
|
|
SrvReadSize = LARGEST_READANDX;
|
|
DoingLargeReadX = TRUE;
|
|
} else {
|
|
|
|
SrvReadSize = SmallReadXSize;
|
|
DoingLargeReadX = FALSE;
|
|
}
|
|
|
|
AmountRequestedToRead = (USHORT)MIN( Length, SrvReadSize );
|
|
|
|
//
|
|
// Fill in the context information to be passed to the indication
|
|
// routine.
|
|
//
|
|
|
|
Context.Header.Type = CONTEXT_READ_ANDX;
|
|
Context.Header.TransferSize =
|
|
sizeof(REQ_READ_ANDX) + sizeof(RESP_READ_ANDX) + AmountRequestedToRead;
|
|
|
|
Context.ReceiveIrp = NULL;
|
|
Context.DataMdl = NULL;
|
|
Context.ReceiveSmbBuffer = NULL;
|
|
Context.ReceiveLength = 0;
|
|
Context.ReceivePosted = FALSE;
|
|
|
|
//
|
|
// Allocate an SMB buffer for the read operation.
|
|
//
|
|
|
|
if ((SendSmbBuffer = RdrAllocateSMBBuffer())==NULL) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto ReturnError;
|
|
}
|
|
|
|
//
|
|
// Also allocate one to hold the response SMB buffer header.
|
|
//
|
|
|
|
if ((Context.ReceiveSmbBuffer = RdrAllocateSMBBuffer())==NULL) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto ReturnError;
|
|
}
|
|
|
|
ASSERT (AmountRequestedToRead <= 0xffff);
|
|
|
|
Smb = (PSMB_HEADER )(SendSmbBuffer->Buffer);
|
|
|
|
Smb->Command = SMB_COM_READ_ANDX;
|
|
|
|
RdrSmbScrounge(Smb, Server, FALSE, FALSE, FALSE);
|
|
|
|
//
|
|
// Flag that this I/O is paging I/O to allow the server to
|
|
// function correctly when loading an executable over the net.
|
|
//
|
|
|
|
if (FlagOn(Irp->Flags, IRP_PAGING_IO)) {
|
|
SmbPutAlignedUshort(&Smb->Flags2, SMB_FLAGS2_PAGING_IO);
|
|
}
|
|
|
|
Read = (PREQ_READ_ANDX )(Smb+1);
|
|
|
|
Read->AndXCommand = SMB_COM_NO_ANDX_COMMAND;
|
|
Read->AndXReserved = 0;
|
|
SmbPutUshort(&Read->AndXOffset, 0);
|
|
|
|
SmbPutUshort(&Read->Fid, Icb->FileId);
|
|
SmbPutUshort(&Read->MaxCount, (USHORT )(AmountRequestedToRead & 0xffff));
|
|
SmbPutUshort(&Read->MinCount, (USHORT )(AmountRequestedToRead & 0xffff));
|
|
SmbPutUlong(&Read->Timeout, 0xffffffff);
|
|
SmbPutUshort(&Read->Remaining, (USHORT )MIN(0xffff, Length));
|
|
SmbPutUlong(&Read->Offset, ReadOffset.LowPart);
|
|
|
|
// if (ReadOffset.HighPart != 0) {
|
|
if (Server->Capabilities & DF_NT_SMBS) {
|
|
PREQ_NT_READ_ANDX NtRead = (PREQ_NT_READ_ANDX )Read;
|
|
|
|
NtRead->WordCount = 12;
|
|
|
|
SmbPutUlong(&NtRead->OffsetHigh, ReadOffset.HighPart);
|
|
SmbPutUshort(&NtRead->ByteCount, 0);
|
|
|
|
SmbPutUshort(&NtRead->ByteCount, 0);
|
|
|
|
//
|
|
// Set the number of bytes to send in this request.
|
|
//
|
|
|
|
SendSmbBuffer->Mdl->ByteCount = sizeof(SMB_HEADER)+sizeof(REQ_NT_READ_ANDX);
|
|
} else {
|
|
Read->WordCount = 10;
|
|
|
|
SmbPutUshort(&Read->ByteCount, 0);
|
|
|
|
//
|
|
// Set the number of bytes to send in this request.
|
|
//
|
|
|
|
SendSmbBuffer->Mdl->ByteCount = sizeof(SMB_HEADER)+sizeof(REQ_NT_READ_ANDX);
|
|
}
|
|
|
|
dprintf(DPRT_READWRITE, ("Read %x bytes, %x remaining (%lx), offset %lx\n", SmbGetUshort(&Read->MaxCount), SmbGetUshort(&Read->Remaining),
|
|
Length, ReadOffset.LowPart));
|
|
|
|
|
|
//
|
|
// Allocate an MDL large enough to hold this piece of the
|
|
// request.
|
|
//
|
|
|
|
Context.DataMdl = IoAllocateMdl((PCHAR )Irp->UserBuffer + TotalReadSoFar,
|
|
AmountRequestedToRead, // Length
|
|
FALSE, // Secondary Buffer
|
|
FALSE, // Charge Quota
|
|
NULL);
|
|
|
|
if (Context.DataMdl == NULL) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto ReturnError;
|
|
}
|
|
|
|
//
|
|
// If there is no MDL for this read, probe the data MDL to lock it's
|
|
// pages down.
|
|
//
|
|
// Otherwise, use the data MDL as a partial MDL and lock the pages
|
|
// accordingly
|
|
//
|
|
|
|
if (Irp->MdlAddress == NULL) {
|
|
|
|
try {
|
|
|
|
MmProbeAndLockPages(Context.DataMdl, Irp->RequestorMode, IoWriteAccess);
|
|
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
|
|
Status = GetExceptionCode();
|
|
goto ReturnError;
|
|
}
|
|
|
|
DataMdlLocked = TRUE;
|
|
} else {
|
|
|
|
IoBuildPartialMdl(Irp->MdlAddress, Context.DataMdl,
|
|
(PCHAR )Irp->UserBuffer + TotalReadSoFar,
|
|
AmountRequestedToRead);
|
|
|
|
|
|
}
|
|
|
|
//
|
|
// Since we are allocating our own IRP for this receive operation,
|
|
// we need to reference the connection object to make sure that it
|
|
// doesn't go away during the receive operation.
|
|
//
|
|
|
|
KeInitializeEvent(&Context.ReceiveCompleteEvent, NotificationEvent, TRUE);
|
|
|
|
Status = RdrReferenceTransportConnection(Server);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
goto ReturnError;
|
|
}
|
|
|
|
ConnectionObjectReferenced = TRUE;
|
|
|
|
Context.ReceiveIrp = ALLOCATE_IRP(
|
|
Server->ConnectionContext->ConnectionObject,
|
|
NULL,
|
|
10,
|
|
&Context
|
|
);
|
|
|
|
if (Context.ReceiveIrp == NULL) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto ReturnError;
|
|
}
|
|
|
|
//
|
|
// Now link this new MDL into the SMB buffer we allocated for
|
|
// the receive.
|
|
//
|
|
|
|
Context.ReceiveSmbBuffer->Mdl->Next = Context.DataMdl;
|
|
|
|
if ((Icb->Type == NamedPipe) &&
|
|
!(Icb->u.p.PipeState & SMB_PIPE_NOWAIT)) {
|
|
Flags |= NT_LONGTERM;
|
|
}
|
|
|
|
RdrStatistics.ReadSmbs += 1;
|
|
|
|
Status = RdrNetTranceiveWithCallback(Flags,
|
|
Irp,
|
|
Icb->Fcb->Connection,
|
|
SendSmbBuffer->Mdl,
|
|
&Context,
|
|
ReadAndXCallback,
|
|
Icb->Se,
|
|
NULL);
|
|
if ( NT_SUCCESS(Status) ) {
|
|
if (Context.ReceivePosted) {
|
|
Status = RdrMapSmbError((PSMB_HEADER )Context.ReceiveSmbBuffer->Buffer, Server);
|
|
}
|
|
} else {
|
|
if (Status == STATUS_INVALID_HANDLE) {
|
|
RdrInvalidateFileId(Icb->NonPagedFcb, Icb->FileId);
|
|
}
|
|
}
|
|
|
|
if (!NT_ERROR(Status)) {
|
|
|
|
//
|
|
// If we had to post a receive to get the data for this read,
|
|
// get the bytes read out of the incoming SMB.
|
|
//
|
|
|
|
if (Context.ReceivePosted) {
|
|
ReadResponse = (PRESP_READ_ANDX )(((PSMB_HEADER )Context.ReceiveSmbBuffer->Buffer)+1);
|
|
|
|
*AmountActuallyRead = SmbGetUshort(&ReadResponse->DataLength);
|
|
|
|
} else {
|
|
|
|
//
|
|
// Otherwise, we can figure out how much was read from the
|
|
// indication data.
|
|
//
|
|
|
|
*AmountActuallyRead = Context.ReceiveLength;
|
|
|
|
}
|
|
|
|
if( DoingLargeReadX ) {
|
|
*AllDataRead = (BOOLEAN )(((USHORT)*AmountActuallyRead) >= SmallReadXSize);
|
|
} else {
|
|
*AllDataRead = (BOOLEAN )(((USHORT)*AmountActuallyRead) == AmountRequestedToRead);
|
|
}
|
|
|
|
if (ARGUMENT_PRESENT(BytesRemainingToBeRead)) {
|
|
*BytesRemainingToBeRead = Context.BytesRemainingToBeRead;
|
|
}
|
|
|
|
if ( Status != STATUS_BUFFER_OVERFLOW &&
|
|
(Icb->Type == NamedPipe)) {
|
|
|
|
//
|
|
// The server did not overflow the buffer so stop submitting
|
|
// core reads to the server. This is usually only a problem
|
|
// on blocking mode pipes when the data being transferred
|
|
// matches the srvwritesize. In this case the extra read will
|
|
// block.
|
|
//
|
|
|
|
*AllDataRead = FALSE;
|
|
}
|
|
|
|
}
|
|
|
|
ReturnError:
|
|
|
|
if (SendSmbBuffer!=NULL) {
|
|
RdrFreeSMBBuffer(SendSmbBuffer);
|
|
}
|
|
|
|
if (Context.ReceiveSmbBuffer!=NULL) {
|
|
RdrFreeSMBBuffer(Context.ReceiveSmbBuffer);
|
|
}
|
|
|
|
if (Context.ReceiveIrp != NULL) {
|
|
NTSTATUS Status1;
|
|
Status1 = KeWaitForSingleObject(&Context.ReceiveCompleteEvent,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL);
|
|
|
|
FREE_IRP( Context.ReceiveIrp, 14, &Context );
|
|
|
|
}
|
|
|
|
if (Context.DataMdl != NULL) {
|
|
|
|
if (DataMdlLocked) {
|
|
MmUnlockPages(Context.DataMdl);
|
|
}
|
|
|
|
IoFreeMdl(Context.DataMdl);
|
|
}
|
|
|
|
if (ConnectionObjectReferenced) {
|
|
RdrDereferenceTransportConnection(Server);
|
|
}
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
|
|
STANDARD_CALLBACK_HEADER(
|
|
ReadAndXCallback
|
|
)
|
|
|
|
/*++
|
|
|
|
ReadAndXCallback - Indication callback for user request
|
|
|
|
|
|
Routine Description:
|
|
|
|
This routine is invoked by either the receive based indication lookahead
|
|
routine from the transport, or by the connection invalidating
|
|
code.
|
|
|
|
Arguments:
|
|
|
|
Irp - Pointer to the I/O request packet from the transport
|
|
IncomingSmb - Pointer to incoming SMB buffer
|
|
MpxTable - Mpx Table entry for request.
|
|
Context - Context information passed into NetTranceiveNoWait
|
|
ErrorIndicator - TRUE if the network request was in error.
|
|
NetworkErrorCode - Error code if request completed with network error
|
|
|
|
Return Value:
|
|
|
|
Return value to be returned from receive indication routine.
|
|
|
|
|
|
Note:
|
|
|
|
This routine can be called for two different reasons. The
|
|
first (and most common) reason is when the receive indication event
|
|
notification comes from the server for this request. In that case,
|
|
this routine should format up a receive to read the response to the
|
|
request and pass the request to the transport to complete the
|
|
request.
|
|
|
|
If the connection is dropped from the transport, the code
|
|
that walks the multiplex table completing requests will call
|
|
this routine with the ErrorIndicator flag set to TRUE, and the
|
|
NetworkErrorCode field set to the error from the transport.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PREAD_ANDX_CONTEXT Context = Ctx;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PRESP_READ_ANDX ReadAndXResponse;
|
|
|
|
DISCARDABLE_CODE(RdrFileDiscardableSection);
|
|
|
|
ASSERT(Context->Header.Type == CONTEXT_READ_ANDX);
|
|
|
|
ASSERT(MpxEntry->Signature == STRUCTURE_SIGNATURE_MPX_ENTRY);
|
|
|
|
Context->Header.ErrorType = NoError; // Assume no error at first
|
|
|
|
if (ErrorIndicator) {
|
|
Context->Header.ErrorType = NetError;
|
|
Context->Header.ErrorCode = NetworkErrorCode;
|
|
goto ReturnStatus;
|
|
}
|
|
|
|
Status = RdrMapSmbError(Smb, Server);
|
|
|
|
if (Status == STATUS_BUFFER_OVERFLOW) {
|
|
|
|
//
|
|
// Don't set ErrorType or ErrorCode in the context since we
|
|
// want to pass the ReceiveIrp to the transport.
|
|
//
|
|
|
|
NOTHING;
|
|
|
|
} else if (!NT_SUCCESS(Status)) {
|
|
Context->Header.ErrorType = SMBError;
|
|
Context->Header.ErrorCode = Status;
|
|
goto ReturnStatus;
|
|
}
|
|
|
|
//
|
|
// If we are doing a Read&X and the entire data is present in the
|
|
// indication, just copy the users data out of the indication data, and
|
|
// return without asking the transport to do the copy.
|
|
//
|
|
|
|
ReadAndXResponse = (PRESP_READ_ANDX) (Smb+1);
|
|
|
|
if (((ULONG)(SmbGetUshort(&ReadAndXResponse->DataOffset)) > MpxEntry->SLE->BufferSize) ||
|
|
(SmbGetUshort(&ReadAndXResponse->DataOffset) > SMB_BUFFER_SIZE)) {
|
|
|
|
//
|
|
// This SMB is bogus (the data offset starts beyond the negotiated
|
|
// buffer size, or the read offset won't fit into an SMB buffer.
|
|
//
|
|
// Drop the VC and return the error to the caller.
|
|
//
|
|
|
|
Context->Header.ErrorType = NetError;
|
|
|
|
Context->Header.ErrorCode = STATUS_UNEXPECTED_NETWORK_ERROR;
|
|
|
|
RdrQueueServerDisconnection(MpxEntry->SLE, RdrMapNetworkError(STATUS_UNEXPECTED_NETWORK_ERROR));
|
|
|
|
//
|
|
// Log the AndX data into the buffer to indicate what went wrong.
|
|
//
|
|
|
|
RdrWriteErrorLogEntry(Server,
|
|
IO_ERR_PROTOCOL,
|
|
EVENT_RDR_INVALID_REPLY,
|
|
STATUS_UNEXPECTED_NETWORK_ERROR,
|
|
ReadAndXResponse,
|
|
(USHORT)*SmbLength
|
|
);
|
|
goto ReturnStatus;
|
|
|
|
}
|
|
|
|
Context->BytesRemainingToBeRead = SmbGetUshort(&ReadAndXResponse->Remaining);
|
|
|
|
if (!NT_ERROR(Status) &&
|
|
(*SmbLength >= (ULONG)(SmbGetUshort(&ReadAndXResponse->DataOffset)+SmbGetUshort(&ReadAndXResponse->DataLength)))) {
|
|
PVOID UsersBuffer;
|
|
|
|
PVOID OffsetInSMB;
|
|
|
|
//
|
|
// If this didn't work, flag the error to return to the caller.
|
|
//
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
Context->Header.ErrorType = SMBError;
|
|
Context->Header.ErrorCode = Status;
|
|
}
|
|
|
|
UsersBuffer = MmGetSystemAddressForMdl(Context->DataMdl);
|
|
|
|
//
|
|
// The offset in the SMB of the data is in the SMB header.
|
|
//
|
|
|
|
OffsetInSMB = ((PCHAR)Smb)+SmbGetUshort(&ReadAndXResponse->DataOffset);
|
|
|
|
//
|
|
// Copy the users data into their buffer and we're done.
|
|
//
|
|
|
|
Context->ReceiveLength = SmbGetUshort(&ReadAndXResponse->DataLength);
|
|
|
|
//
|
|
// Ask TDI to copy the data into the users buffer.
|
|
//
|
|
|
|
TdiCopyLookaheadData(UsersBuffer, OffsetInSMB, Context->ReceiveLength, ReceiveFlags);
|
|
|
|
goto ReturnStatus;
|
|
}
|
|
|
|
if (ARGUMENT_PRESENT(Context->ReceiveIrp)) {
|
|
ULONG SmbSize;
|
|
|
|
Context->ReceivePosted = TRUE;
|
|
|
|
Context->Header.ErrorType = ReceiveIrpProcessing;
|
|
|
|
//
|
|
// In this case, we take no data out of the SMB.
|
|
//
|
|
|
|
*SmbLength = 0;
|
|
|
|
//
|
|
// We are about to return this IRP, so activate the receive complete
|
|
// event in the context header so that ReadAndX will wait
|
|
// until this receive completes (in the case that we might time out
|
|
// the VC after this receive completes, we don't want to free the IRP
|
|
// to early).
|
|
//
|
|
|
|
KeClearEvent(&Context->ReceiveCompleteEvent);
|
|
|
|
SmbSize = SmbGetUshort(&ReadAndXResponse->DataOffset);
|
|
|
|
//
|
|
// Set the size of the data to be received into the SMB buffer.
|
|
//
|
|
// Please note that NT servers return 0 as the data offset if there
|
|
// is no data, so we have to at least receive the header.
|
|
//
|
|
|
|
|
|
SmbSize = MAX(SmbSize, (ULONG)(sizeof(SMB_HEADER)+FIELD_OFFSET(RESP_READ_ANDX, Buffer[0])));
|
|
|
|
Context->ReceiveSmbBuffer->Mdl->ByteCount = SmbSize;
|
|
|
|
RdrBuildReceive(Context->ReceiveIrp, MpxEntry->SLE,
|
|
ReadAndXComplete, Context, Context->ReceiveSmbBuffer->Mdl,
|
|
SmbSize+SmbGetUshort(&ReadAndXResponse->DataLength));
|
|
|
|
Context->ReceiveSmbBuffer->Mdl->Next = Context->DataMdl;
|
|
|
|
//
|
|
// This gets kinda wierd.
|
|
//
|
|
// Since this IRP is going to be completed by the transport without
|
|
// ever going to IoCallDriver, we have to update the stack location
|
|
// to make the transports stack location the current stack location.
|
|
//
|
|
// Please note that this means that any transport provider that uses
|
|
// IoCallDriver to re-submit it's requests at indication time will
|
|
// break badly because of this code....
|
|
//
|
|
|
|
IoSetNextIrpStackLocation( Context->ReceiveIrp );
|
|
|
|
//
|
|
// We had better have enough to handle this request already lined up for
|
|
// the receive.
|
|
//
|
|
|
|
ASSERT ((USHORT)Context->ReceiveIrp->MdlAddress->Next->ByteCount >= SmbGetUshort(&ReadAndXResponse->DataLength));
|
|
|
|
RdrStartReceiveForMpxEntry (MpxEntry, Context->ReceiveIrp);
|
|
|
|
*Irp = Context->ReceiveIrp;
|
|
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
|
|
|
|
ReturnStatus:
|
|
//
|
|
// Set the event to the SIGNALED state
|
|
//
|
|
|
|
KeSetEvent(&Context->Header.KernelEvent, IO_NETWORK_INCREMENT, FALSE); // Wake up process.
|
|
|
|
return STATUS_SUCCESS; // We're done, eat response and return
|
|
|
|
if (SmbLength||MpxEntry||Server);
|
|
}
|
|
|
|
DBGSTATIC
|
|
NTSTATUS
|
|
ReadAndXComplete (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Ctx
|
|
)
|
|
/*++
|
|
|
|
ReadAndXComplete - Final completion for user request.
|
|
|
|
Routine Description:
|
|
|
|
This routine is called on final completion of the TDI_Receive
|
|
request from the transport. If the request completed successfully,
|
|
this routine will complete the request with no error, if the receive
|
|
completed with an error, it will flag the error and complete the
|
|
request.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Device structure that the request completed on.
|
|
Irp - The Irp that completed.
|
|
Context - Context information for completion.
|
|
|
|
Return Value:
|
|
|
|
Return value to be returned from receive indication routine.
|
|
--*/
|
|
|
|
|
|
{
|
|
PREAD_ANDX_CONTEXT Context = Ctx;
|
|
|
|
dprintf(DPRT_SMB, ("ReadAndXComplete. Irp: %lx, Context: %lx\n", Irp, Context));
|
|
|
|
ASSERT(Context->Header.Type == CONTEXT_READ_ANDX);
|
|
|
|
RdrCompleteReceiveForMpxEntry (Context->Header.MpxTableEntry, Irp);
|
|
|
|
if (NT_SUCCESS(Irp->IoStatus.Status)) {
|
|
|
|
//
|
|
// Setting ReceiveIrpProcessing will cause the checks in
|
|
// RdrNetTranceive to check the incoming SMB for errors.
|
|
//
|
|
|
|
Context->Header.ErrorType = ReceiveIrpProcessing;
|
|
|
|
Context->ReceiveLength = Irp->IoStatus.Information;
|
|
|
|
SMBTRACE_RDR( Irp->MdlAddress );
|
|
|
|
ExInterlockedAddLargeStatistic(
|
|
&RdrStatistics.BytesReceived,
|
|
Irp->IoStatus.Information );
|
|
|
|
} else {
|
|
|
|
RdrStatistics.FailedCompletionOperations += 1;
|
|
|
|
Context->Header.ErrorType = NetError;
|
|
Context->Header.ErrorCode=RdrMapNetworkError(Irp->IoStatus.Status);
|
|
|
|
}
|
|
|
|
//
|
|
// Mark that the kernel event indicating that this I/O operation has
|
|
// completed is done.
|
|
//
|
|
// Please note that we need TWO events here. The first event is
|
|
// set to the signalled state when the multiplexed exchange is
|
|
// completed, while the second is set to the signalled status when
|
|
// this receive request has completed,
|
|
//
|
|
// The KernelEvent MUST BE SET FIRST, THEN the ReceiveCompleteEvent.
|
|
// This is because the KernelEvent may already be set, in which case
|
|
// setting the ReceiveCompleteEvent first would let the thread that's
|
|
// waiting on the events run, and delete the KernelEvent before we
|
|
// set it.
|
|
//
|
|
|
|
KeSetEvent(&Context->Header.KernelEvent, IO_NETWORK_INCREMENT, FALSE);
|
|
KeSetEvent(&Context->ReceiveCompleteEvent, IO_NETWORK_INCREMENT, FALSE);
|
|
|
|
//
|
|
// Short circuit I/O completion on this request now.
|
|
//
|
|
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
|
|
if (DeviceObject);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
RawRead (
|
|
IN PIRP Irp,
|
|
IN PICB Icb,
|
|
IN LARGE_INTEGER ReadOffset,
|
|
IN ULONG Length,
|
|
IN ULONG TotalDataReadSoFar,
|
|
OUT PBOOLEAN AllDataRead,
|
|
OUT PULONG AmountActuallyRead
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Read data from the file using raw read protocols.
|
|
|
|
|
|
Arguments:
|
|
|
|
IN PIRP Irp - Supplies an IRP to use for the raw read request.
|
|
IN PICB Icb - Supplies an ICB for the file to read.
|
|
IN LARGE_INTEGER ReadOffset - Supplies the offset to read from in the file.
|
|
IN ULONG Length - Supplies the total number of bytes to read.
|
|
IN ULONG TotalDataReadSoFar - Supplies the # of bytes read so far.
|
|
OUT PBOOLEAN AllDataRead - Returns true if all the data requested was read
|
|
OUT PULONG AmountActuallyRead - Returns the number of bytes read.
|
|
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Status of operation (if there was a network error).
|
|
If AmountActuallyRead is 0, retry using core protocols.
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PSERVERLISTENTRY Server = Icb->Fcb->Connection->Server;
|
|
BOOLEAN ResourceAcquired = FALSE;
|
|
PSMB_BUFFER SmbBuffer = NULL;
|
|
PSMB_HEADER Smb;
|
|
PREQ_READ_RAW RawRead;
|
|
PMDL ReceiveMdl = NULL;
|
|
BOOLEAN ReceiveMdlLocked = FALSE;
|
|
LARGE_INTEGER startTime;
|
|
|
|
PAGED_CODE();
|
|
|
|
*AmountActuallyRead = 0;
|
|
|
|
try {
|
|
//
|
|
// If the user isn't reading at least a reasonable amount of data
|
|
// from the file, don't try raw.
|
|
//
|
|
|
|
if (Length < Server->BufferSize * RAW_THRESHOLD) {
|
|
try_return(Status);
|
|
}
|
|
|
|
//
|
|
// If this I/O will take too long, don't use raw
|
|
//
|
|
if( Length > Server->RawReadMaximum ) {
|
|
try_return( Status );
|
|
}
|
|
|
|
//
|
|
// If we are trying to do a read past the end of the nominal
|
|
// end of file, don't bother with raw I/O, try core.
|
|
//
|
|
|
|
if (ReadOffset.QuadPart >= RdrQueryFileSize(Icb->Fcb).QuadPart) {
|
|
try_return(Status);
|
|
}
|
|
|
|
//
|
|
// Try to acquire the server's raw resource. If we could not get the
|
|
// resource, return saying the raw I/O failed.
|
|
//
|
|
|
|
//
|
|
// Please note that we CANNOT block for this raw I/O, since a create
|
|
// might be in progress waiting on the FCB lock, and thus we won't
|
|
// be able to get the raw resource (since the create owns it shared),
|
|
// and thus we will deadlock.
|
|
//
|
|
|
|
if (!ExAcquireResourceExclusive(&Server->RawResource, FALSE)) {
|
|
#if 1
|
|
try_return(Status);
|
|
#else
|
|
LARGE_INTEGER delay;
|
|
delay.QuadPart = -10*1000*1; // 1 millisecond (wake up at next tick)
|
|
KeDelayExecutionThread( KernelMode, FALSE, &delay );
|
|
if (!ExAcquireResourceExclusive(&Server->RawResource, FALSE)) {
|
|
try_return(Status);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
ResourceAcquired = TRUE;
|
|
|
|
//
|
|
// At this point, we have locked out all access to the remote server.
|
|
// We are guaranteed that no SMB's will be submitted until we release
|
|
// the resource.
|
|
//
|
|
|
|
SmbBuffer = RdrAllocateSMBBuffer();
|
|
|
|
if (SmbBuffer == NULL) {
|
|
try_return(Status = STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
|
|
ReceiveMdl = IoAllocateMdl((PCHAR )Irp->UserBuffer+TotalDataReadSoFar,
|
|
Length,
|
|
FALSE, FALSE, NULL);
|
|
|
|
if (ReceiveMdl == NULL) {
|
|
try_return(Status = STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
//
|
|
// If there is no MDL for this read, probe the data MDL to lock it's
|
|
// pages down.
|
|
//
|
|
// Otherwise, use the data MDL as a partial MDL and lock the pages
|
|
// accordingly
|
|
//
|
|
|
|
if (Irp->MdlAddress == NULL) {
|
|
|
|
try {
|
|
|
|
MmProbeAndLockPages(ReceiveMdl, Irp->RequestorMode, IoWriteAccess);
|
|
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
|
|
try_return(Status = GetExceptionCode());
|
|
|
|
}
|
|
|
|
ReceiveMdlLocked = TRUE;
|
|
} else {
|
|
|
|
IoBuildPartialMdl(Irp->MdlAddress, ReceiveMdl,
|
|
(PCHAR )Irp->UserBuffer + TotalDataReadSoFar,
|
|
Length);
|
|
|
|
|
|
}
|
|
|
|
ASSERT (Length <= 0xffff);
|
|
|
|
Smb = (PSMB_HEADER)SmbBuffer->Buffer;
|
|
|
|
RawRead = (PREQ_READ_RAW) (Smb+1);
|
|
|
|
Smb->Command = SMB_COM_READ_RAW;
|
|
|
|
RdrSmbScrounge(Smb, Server, FALSE, FALSE, FALSE);
|
|
//
|
|
// Flag that this I/O is paging I/O to allow the server to
|
|
// function correctly when loading an executable over the net.
|
|
//
|
|
|
|
if (FlagOn(Irp->Flags, IRP_PAGING_IO)) {
|
|
SmbPutAlignedUshort(&Smb->Flags2, SMB_FLAGS2_PAGING_IO);
|
|
}
|
|
|
|
SmbPutUshort(&RawRead->Fid, Icb->FileId);
|
|
|
|
SmbPutUlong(&RawRead->Offset, ReadOffset.LowPart);
|
|
|
|
if (Icb->Fcb->Connection->Server->Capabilities & DF_NT_SMBS) {
|
|
PREQ_NT_READ_RAW NtReadRaw = (PREQ_NT_READ_RAW )RawRead;
|
|
|
|
NtReadRaw->WordCount = 10;
|
|
|
|
SmbPutUlong(&NtReadRaw->OffsetHigh, ReadOffset.HighPart);
|
|
|
|
SmbPutUshort(&NtReadRaw->ByteCount, 0);
|
|
|
|
SmbBuffer->Mdl->ByteCount = sizeof(SMB_HEADER) + FIELD_OFFSET(REQ_NT_READ_RAW, Buffer[0]);
|
|
} else {
|
|
RawRead->WordCount = 8;
|
|
|
|
SmbPutUshort(&RawRead->ByteCount, 0);
|
|
|
|
SmbBuffer->Mdl->ByteCount = sizeof(SMB_HEADER) + FIELD_OFFSET(REQ_READ_RAW, Buffer[0]);
|
|
|
|
}
|
|
|
|
SmbPutUshort(&RawRead->MaxCount, (USHORT )Length);
|
|
|
|
SmbPutUshort(&RawRead->MinCount, (USHORT )0);
|
|
|
|
SmbPutUlong(&RawRead->Timeout, 0L);
|
|
|
|
SmbPutUshort(&RawRead->Reserved, 0);
|
|
|
|
//
|
|
// Exchange this SMB with the server as a raw SMB.
|
|
//
|
|
RdrStatistics.ReadSmbs += 1;
|
|
|
|
KeQuerySystemTime( &startTime );
|
|
|
|
Status = RdrRawTranceive(NT_NORMAL | NT_DONTSCROUNGE,
|
|
Irp,
|
|
Icb->Fcb->Connection,
|
|
Icb->Se,
|
|
SmbBuffer->Mdl,
|
|
ReceiveMdl,
|
|
AmountActuallyRead);
|
|
|
|
*AllDataRead = (BOOLEAN )(*AmountActuallyRead == Length);
|
|
|
|
if( *AllDataRead ) {
|
|
LARGE_INTEGER transmissionTime, endTime;
|
|
|
|
KeQuerySystemTime( &endTime );
|
|
|
|
transmissionTime.QuadPart = endTime.QuadPart - startTime.QuadPart;
|
|
if( transmissionTime.LowPart > RdrRawTimeLimit * 10 * 1000 * 1000 ) {
|
|
ULONG newMaximum;
|
|
|
|
//
|
|
// This transmission took too long. Trim back Server->RawReadMaximum
|
|
//
|
|
newMaximum = (Length * RdrRawTimeLimit * 10 * 1000 * 1000) /
|
|
transmissionTime.LowPart;
|
|
|
|
if( newMaximum ) {
|
|
Server->RawReadMaximum = newMaximum;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
try_exit:NOTHING;
|
|
} finally {
|
|
|
|
if (SmbBuffer) {
|
|
RdrFreeSMBBuffer(SmbBuffer);
|
|
}
|
|
|
|
if (ReceiveMdl) {
|
|
if (ReceiveMdlLocked) {
|
|
MmUnlockPages(ReceiveMdl);
|
|
}
|
|
IoFreeMdl(ReceiveMdl);
|
|
}
|
|
|
|
if (ResourceAcquired) {
|
|
ExReleaseResource(&Server->RawResource);
|
|
}
|
|
|
|
if ((NT_SUCCESS(Status)) &&
|
|
(AmountActuallyRead == 0)) {
|
|
RdrStatistics.RawReadsDenied += 1;
|
|
} else {
|
|
if (Status == STATUS_INVALID_HANDLE) {
|
|
RdrInvalidateFileId(Icb->NonPagedFcb, Icb->FileId);
|
|
}
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
|
|
}
|
|
|