NT4/private/ntos/rdr/read.c
2020-09-30 17:12:29 +02:00

2849 lines
81 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
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;
}