Windows NT 4.0 source code leak
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.
 
 
 
 
 
 

1224 lines
34 KiB

/*++
Copyright (c) 1990 Microsoft Corporation
Module Name:
fsdsmb.c
Abstract:
This module implements SMB processing routines and their support
routines for the File System Driver of the LAN Manager server.
*** This module must be nonpageable.
Author:
Chuck Lenzmeier (chuckl) 19-Mar-1990
Revision History:
--*/
//
// This module is laid out as follows:
// Includes
// Local #defines
// Local type definitions
// Forward declarations of local functions
// SMB processing routines
// Restart routines and other support routines
//
#include "precomp.h"
#pragma hdrstop
VOID SRVFASTCALL
SrvFspRestartLargeReadAndXComplete(
IN OUT PWORK_CONTEXT WorkContext
);
#ifdef ALLOC_PRAGMA
//#pragma alloc_text( PAGE8FIL, SrvFsdRestartRead )
#pragma alloc_text( PAGE8FIL, SrvFsdRestartReadAndX )
#pragma alloc_text( PAGE8FIL, SrvFsdRestartWrite )
#pragma alloc_text( PAGE8FIL, SrvFsdRestartWriteAndX )
#pragma alloc_text( PAGE, SrvFspRestartLargeReadAndXComplete )
#endif
VOID SRVFASTCALL
SrvFsdRestartRead (
IN OUT PWORK_CONTEXT WorkContext
)
/*++
Routine Description:
Processes file read completion for a Read SMB.
Arguments:
WorkContext - Supplies a pointer to the work context block
describing server-specific context for the request.
Return Value:
None.
--*/
{
PREQ_READ request;
PRESP_READ response;
NTSTATUS status;
PRFCB rfcb;
SHARE_TYPE shareType;
KIRQL oldIrql;
USHORT readLength;
UNLOCKABLE_CODE( 8FIL );
IF_DEBUG(FSD2) SrvPrint0( " - SrvFsdRestartRead\n" );
//
// Get the request and response parameter pointers.
//
request = (PREQ_READ)WorkContext->RequestParameters;
response = (PRESP_READ)WorkContext->ResponseParameters;
//
// Get the file pointer.
//
rfcb = WorkContext->Rfcb;
shareType = rfcb->ShareType;
IF_DEBUG(FSD2) {
SrvPrint2( " connection 0x%lx, RFCB 0x%lx\n",
WorkContext->Connection, rfcb );
}
//
// If the read failed, set an error status in the response header.
// (If we tried to read entirely beyond the end of file, we return a
// normal response indicating that nothing was read.)
//
status = WorkContext->Irp->IoStatus.Status;
readLength = (USHORT)WorkContext->Irp->IoStatus.Information;
if ( status == STATUS_BUFFER_OVERFLOW && shareType == ShareTypePipe ) {
//
// If this is an named pipe and the error is
// STATUS_BUFFER_OVERFLOW, set the error in the smb header, but
// return all the data to the client.
//
SrvSetBufferOverflowError( WorkContext );
} else if ( !NT_SUCCESS(status) ) {
if ( status != STATUS_END_OF_FILE ) {
IF_DEBUG(ERRORS) SrvPrint1( "Read failed: %X\n", status );
if ( KeGetCurrentIrql() >= DISPATCH_LEVEL ) {
WorkContext->FspRestartRoutine = SrvFsdRestartRead;
QUEUE_WORK_TO_FSP( WorkContext );
} else {
SrvSetSmbError( WorkContext, status );
SrvFsdSendResponse( WorkContext );
}
IF_DEBUG(FSD2) SrvPrint0( "SrvFsdRestartRead complete\n" );
return;
} else {
readLength = 0;
}
}
#ifdef SLMDBG
{
PRFCB_TRACE entry;
PCHAR readAddress;
ACQUIRE_GLOBAL_SPIN_LOCK( Fsd, &oldIrql );
rfcb->OperationCount++;
entry = &rfcb->Trace[rfcb->NextTrace];
if ( ++rfcb->NextTrace == SLMDBG_TRACE_COUNT ) {
rfcb->NextTrace = 0;
rfcb->TraceWrapped = TRUE;
}
RELEASE_GLOBAL_SPIN_LOCK( Fsd, oldIrql );
entry->Command = WorkContext->NextCommand;
KeQuerySystemTime( &entry->Time );
entry->Data.ReadWrite.Offset = SmbGetUlong( &request->Offset );
entry->Data.ReadWrite.Length = readLength;
readAddress = (PCHAR)response->Buffer;
}
#endif
//
// The read completed successfully. If this is a disk file, update
// the file position.
//
if (shareType == ShareTypeDisk) {
rfcb->CurrentPosition = SmbGetUlong( &request->Offset ) + readLength;
}
//
// Save the count of bytes read, to be used to update the server
// statistics database.
//
UPDATE_READ_STATS( WorkContext, readLength );
//
// Build the response message.
//
response->WordCount = 5;
SmbPutUshort( &response->Count, readLength );
RtlZeroMemory( (PVOID)&response->Reserved[0], sizeof(response->Reserved) );
SmbPutUshort(
&response->ByteCount,
(USHORT)(readLength + FIELD_OFFSET(RESP_READ,Buffer[0]) -
FIELD_OFFSET(RESP_READ,BufferFormat))
);
response->BufferFormat = SMB_FORMAT_DATA;
SmbPutUshort( &response->DataLength, readLength );
WorkContext->ResponseParameters = NEXT_LOCATION(
response,
RESP_READ,
readLength
);
//
// Processing of the SMB is complete. Send the response.
//
SrvFsdSendResponse( WorkContext );
IF_DEBUG(FSD2) SrvPrint0( "SrvFsdRestartRead complete\n" );
return;
} // SrvFsdRestartRead
VOID SRVFASTCALL
SrvFsdRestartReadAndX (
IN OUT PWORK_CONTEXT WorkContext
)
/*++
Routine Description:
Processes file read completion for a ReadAndX SMB.
This routine may be called in the FSD or the FSP. If the chained
command is Close, it will be called in the FSP.
*** This routine cannot look at the original ReadAndX request!
This is because the read data may have overlaid the request.
All necessary information from the request must be stored
in WorkContext->Parameters.ReadAndX.
Arguments:
WorkContext - Supplies a pointer to the work context block
describing server-specific context for the request.
Return Value:
None.
--*/
{
PRESP_READ_ANDX response;
NTSTATUS status;
PRFCB rfcb;
SHARE_TYPE shareType;
KIRQL oldIrql;
PCHAR readAddress;
CLONG bufferOffset;
USHORT readLength;
UNLOCKABLE_CODE( 8FIL );
IF_DEBUG(FSD2) SrvPrint0( " - SrvFsdRestartReadAndX\n" );
//
// Get the response parameter pointer.
//
response = (PRESP_READ_ANDX)WorkContext->ResponseParameters;
//
// Get the file pointer.
//
rfcb = WorkContext->Rfcb;
shareType = rfcb->ShareType;
IF_DEBUG(FSD2) {
SrvPrint2( " connection 0x%lx, RFCB 0x%lx\n",
WorkContext->Connection, rfcb );
}
//
// If the read failed, set an error status in the response header.
// (If we tried to read entirely beyond the end of file, we return a
// normal response indicating that nothing was read.)
//
status = WorkContext->Irp->IoStatus.Status;
readLength = (USHORT)WorkContext->Irp->IoStatus.Information;
if ( status == STATUS_BUFFER_OVERFLOW && shareType == ShareTypePipe ) {
//
// If this is an named pipe and the error is
// STATUS_BUFFER_OVERFLOW, set the error in the smb header, but
// return all the data to the client.
//
SrvSetBufferOverflowError( WorkContext );
} else if ( !NT_SUCCESS(status) ) {
if ( status != STATUS_END_OF_FILE ) {
IF_DEBUG(ERRORS) SrvPrint1( "Read failed: %X\n", status );
if ( KeGetCurrentIrql() >= DISPATCH_LEVEL ) {
WorkContext->FspRestartRoutine = SrvFsdRestartReadAndX;
QUEUE_WORK_TO_FSP( WorkContext );
} else {
SrvSetSmbError( WorkContext, status );
SrvFsdSendResponse( WorkContext );
}
IF_DEBUG(FSD2) SrvPrint0( "SrvFsdRestartReadAndX complete\n" );
return;
} else {
readLength = 0;
}
}
//
// The read completed successfully. Generate information about the
// destination of the read data. Find out how much was actually
// read. If none was read, we don't have to worry about the offset.
//
if ( readLength != 0 ) {
readAddress = WorkContext->Parameters.ReadAndX.ReadAddress;
bufferOffset = readAddress - (PCHAR)WorkContext->ResponseHeader;
//
// Save the count of bytes read, to be used to update the server
// statistics database.
//
UPDATE_READ_STATS( WorkContext, readLength );
} else {
readAddress = (PCHAR)response->Buffer;
bufferOffset = 0;
}
#ifdef SLMDBG
{
PRFCB_TRACE entry;
ACQUIRE_GLOBAL_SPIN_LOCK( Fsd, &oldIrql );
rfcb->OperationCount++;
entry = &rfcb->Trace[rfcb->NextTrace];
if ( ++rfcb->NextTrace == SLMDBG_TRACE_COUNT ) {
rfcb->NextTrace = 0;
rfcb->TraceWrapped = TRUE;
}
RELEASE_GLOBAL_SPIN_LOCK( Fsd, oldIrql );
entry->Command = WorkContext->NextCommand;
KeQuerySystemTime( &entry->Time );
entry->Data.ReadWrite.Offset =
WorkContext->Parameters.ReadAndX.ReadOffset.LowPart;
ASSERT (WorkContext->Parameters.ReadAndX.ReadOffset.HighPart == 0);
entry->Data.ReadWrite.Length = readLength;
}
#endif
if (shareType == ShareTypePipe) {
//
// If this is NPFS then, Irp->Overlay.AllocationSize actually
// contains the number bytes left to read on this side of the named
// pipe. Return this information to the client.
//
if (WorkContext->Irp->Overlay.AllocationSize.LowPart != 0) {
SmbPutUshort(
&response->Remaining,
(USHORT)WorkContext->Irp->Overlay.AllocationSize.LowPart - readLength
);
} else {
SmbPutUshort(
&response->Remaining,
0
);
}
} else {
if ( shareType == ShareTypeDisk ) {
//
// If this is a disk file, then update the file position.
//
rfcb->CurrentPosition =
WorkContext->Parameters.ReadAndX.ReadOffset.LowPart +
readLength;
}
SmbPutUshort( &response->Remaining, (USHORT)-1 );
}
//
// Build the response message. (Note that if no data was read, we
// return a byte count of 0 -- we don't add padding.)
//
// *** Note that even though there may have been a chained command,
// we make this the last response in the chain. This is what
// the OS/2 server does. (Sort of -- it doesn't bother to
// update the AndX fields of the response.) Since the only legal
// chained commands are Close and CloseAndTreeDisc, this seems
// like a reasonable thing to do. It does make life easier --
// we don't have to find the end of the read data and write
// another response there. Besides, the read data might have
// completely filled the SMB buffer.
//
response->WordCount = 12;
response->AndXCommand = SMB_COM_NO_ANDX_COMMAND;
response->AndXReserved = 0;
SmbPutUshort( &response->AndXOffset, 0 );
SmbPutUshort( &response->DataCompactionMode, 0 );
SmbPutUshort( &response->Reserved, 0 );
SmbPutUshort( &response->DataLength, (USHORT)readLength );
SmbPutUshort( &response->DataOffset, (USHORT)bufferOffset );
SmbPutUshort( &response->Reserved2, 0 );
RtlZeroMemory( (PVOID)&response->Reserved3[0], sizeof(response->Reserved3) );
SmbPutUshort(
&response->ByteCount,
(USHORT)(readLength + (readAddress - response->Buffer))
);
WorkContext->ResponseParameters = NEXT_LOCATION(
response,
RESP_READ_ANDX,
readLength +
(readAddress - response->Buffer)
);
//
// Processing of the SMB is complete, except that the file may still
// need to be closed. If not, just send the response. If this is a
// ReadAndX and Close, we need to close the file first.
//
// *** Note that other chained commands are illegal, but are ignored
// -- no error is returned.
//
if ( WorkContext->NextCommand != SMB_COM_CLOSE ) {
//
// Not a chained Close. Just send the response.
//
SrvFsdSendResponse( WorkContext );
} else {
ASSERT( KeGetCurrentIrql() < DISPATCH_LEVEL );
//
// Remember the file last write time, to correctly set this on
// close.
//
WorkContext->Parameters.LastWriteTime =
WorkContext->Parameters.ReadAndX.LastWriteTimeInSeconds;
//
// This is a ReadAndX and Close. Call SrvRestartChainedClose to
// do the close and send the response.
//
SrvRestartChainedClose( WorkContext );
}
IF_DEBUG(FSD2) SrvPrint0( "SrvFsdRestartReadAndX complete\n" );
return;
} // SrvFsdRestartReadAndX
/*
* This routine is called at final send completion
*/
VOID SRVFASTCALL
SrvFspRestartLargeReadAndXComplete(
IN OUT PWORK_CONTEXT WorkContext
)
{
NTSTATUS status;
PAGED_CODE();
if( WorkContext->Parameters.ReadAndX.SavedMdl != NULL ) {
WorkContext->ResponseBuffer->Mdl = WorkContext->Parameters.ReadAndX.SavedMdl;
MmPrepareMdlForReuse( WorkContext->ResponseBuffer->PartialMdl );
WorkContext->ResponseBuffer->PartialMdl->Next = NULL;
}
if ( WorkContext->Parameters.ReadAndX.MdlRead == TRUE ) {
//
// Call the Cache Manager to release the MDL chain.
//
if( WorkContext->Parameters.ReadAndX.CacheMdl ) {
//
// Try the fast path first..
//
if( WorkContext->Rfcb->Lfcb->MdlReadComplete == NULL ||
WorkContext->Rfcb->Lfcb->MdlReadComplete(
WorkContext->Rfcb->Lfcb->FileObject,
WorkContext->Parameters.ReadAndX.CacheMdl,
WorkContext->Rfcb->Lfcb->DeviceObject ) == FALSE ) {
//
// Fast path didn't work, try an IRP...
//
status = SrvIssueMdlCompleteRequest( WorkContext, NULL,
WorkContext->Parameters.ReadAndX.CacheMdl,
IRP_MJ_READ,
&WorkContext->Parameters.ReadAndX.ReadOffset,
WorkContext->Parameters.ReadAndX.ReadLength
);
if( !NT_SUCCESS( status ) ) {
//
// At this point, all we can do is complain!
//
SrvLogServiceFailure( SRV_SVC_MDL_COMPLETE, status );
}
}
}
} else {
PMDL mdl = (PMDL)(WorkContext->Parameters.ReadAndX.ReadAddress);
//
// We shortened the byte count if the read returned less data than we asked for
//
mdl->ByteCount = WorkContext->Parameters.ReadAndX.ReadLength;
MmUnlockPages( mdl );
MmPrepareMdlForReuse( mdl );
FREE_HEAP( WorkContext->Parameters.ReadAndX.Buffer );
}
SrvDereferenceWorkItem( WorkContext );
return;
}
/*
* This routine is called when the read completes
*/
VOID SRVFASTCALL
SrvFsdRestartLargeReadAndX (
IN OUT PWORK_CONTEXT WorkContext
)
/*++
Routine Description:
Processes file read completion for a ReadAndX SMB which
is larger than the negotiated buffer size, and is from
a disk file.
There is no follow on command.
Arguments:
WorkContext - Supplies a pointer to the work context block
describing server-specific context for the request.
Return Value:
None.
--*/
{
PRESP_READ_ANDX response = (PRESP_READ_ANDX)WorkContext->ResponseParameters;
USHORT readLength;
NTSTATUS status = WorkContext->Irp->IoStatus.Status;
PRFCB rfcb = WorkContext->Rfcb;
PIRP irp = WorkContext->Irp;
BOOLEAN mdlRead = WorkContext->Parameters.ReadAndX.MdlRead;
UNLOCKABLE_CODE( 8FIL );
if ( !NT_SUCCESS(status) ) {
if( status != STATUS_END_OF_FILE ) {
IF_DEBUG(ERRORS) SrvPrint1( "Read failed: %X\n", status );
//
// We cannot call SrvSetSmbError() at elevated IRQL.
//
if( KeGetCurrentIrql() != 0 ) {
//
// Requeue this routine to come back around at passive level.
// (inefficient, but should be very rare)
//
WorkContext->FspRestartRoutine = SrvFsdRestartLargeReadAndX;
SrvQueueWorkToFspAtDpcLevel( WorkContext );
return;
}
SrvSetSmbError( WorkContext, status );
}
readLength = 0;
} else if( mdlRead ) {
//
// For an MDL read, we have to walk the MDL chain in order to
// determine how much data was read. This is because the
// operation may have happened in multiple steps, with the MDLs
// being chained together. For example, part of the read may
// have been satisfied by the fast path, while the rest was satisfied
// using an IRP
//
PMDL mdl = WorkContext->Irp->MdlAddress;
readLength = 0;
while( mdl != NULL ) {
readLength += (USHORT)MmGetMdlByteCount( mdl );
mdl = mdl->Next;
}
} else {
//
// This was a copy read. The I/O status block has the length.
//
readLength = (USHORT)WorkContext->Irp->IoStatus.Information;
}
//
// Build the response message. (Note that if no data was read, we
// return a byte count of 0 -- we don't add padding.)
//
SmbPutUshort( &response->Remaining, (USHORT)-1 );
response->WordCount = 12;
response->AndXCommand = SMB_COM_NO_ANDX_COMMAND;
response->AndXReserved = 0;
SmbPutUshort( &response->AndXOffset, 0 );
SmbPutUshort( &response->DataCompactionMode, 0 );
SmbPutUshort( &response->Reserved, 0 );
SmbPutUshort( &response->Reserved2, 0 );
RtlZeroMemory( (PVOID)&response->Reserved3[0], sizeof(response->Reserved3) );
SmbPutUshort( &response->DataLength, readLength );
if( readLength == 0 ) {
SmbPutUshort( &response->DataOffset, 0 );
SmbPutUshort( &response->ByteCount, 0 );
WorkContext->Parameters.ReadAndX.PadCount = 0;
} else {
//
// Update the file position.
//
rfcb->CurrentPosition =
WorkContext->Parameters.ReadAndX.ReadOffset.LowPart +
readLength;
//
// Update statistics
//
UPDATE_READ_STATS( WorkContext, readLength );
SmbPutUshort( &response->DataOffset,
(USHORT)(READX_BUFFER_OFFSET + WorkContext->Parameters.ReadAndX.PadCount) );
SmbPutUshort( &response->ByteCount,
(USHORT)( readLength + WorkContext->Parameters.ReadAndX.PadCount ) );
}
//
// We will use two MDLs to describe the packet we're sending -- one
// for the header and parameters, the other for the data.
//
// Handling of the second MDL varies depending on whether we did a copy
// read or an MDL read.
//
//
// Set the first MDL for just the header + pad
//
IoBuildPartialMdl(
WorkContext->ResponseBuffer->Mdl,
WorkContext->ResponseBuffer->PartialMdl,
WorkContext->ResponseBuffer->Buffer,
READX_BUFFER_OFFSET + WorkContext->Parameters.ReadAndX.PadCount
);
//
// Set the overall data length to the header + pad + data
//
WorkContext->ResponseBuffer->DataLength = READX_BUFFER_OFFSET +
WorkContext->Parameters.ReadAndX.PadCount +
readLength;
irp->Cancel = FALSE;
//
// The second MDL depends on the kind of read which we did
//
if( readLength != 0 ) {
if( mdlRead ) {
WorkContext->ResponseBuffer->PartialMdl->Next =
WorkContext->Irp->MdlAddress;
} else {
//
// This was a copy read. The MDL describing the data buffer is in the SMB buffer
//
PMDL mdl = (PMDL)(WorkContext->Parameters.ReadAndX.ReadAddress);
WorkContext->ResponseBuffer->PartialMdl->Next = mdl;
mdl->ByteCount = readLength;
}
}
//
// SrvStartSend2 wants to use WorkContext->ResponseBuffer->Mdl, but
// we want it to use WorkContext->ResponseBuffer->PartialMdl. So switch
// it!
//
WorkContext->Parameters.ReadAndX.SavedMdl = WorkContext->ResponseBuffer->Mdl;
WorkContext->ResponseBuffer->Mdl = WorkContext->ResponseBuffer->PartialMdl;
//
// Send the response!
//
WorkContext->ResponseHeader->Flags |= SMB_FLAGS_SERVER_TO_REDIR;
WorkContext->FspRestartRoutine = SrvFspRestartLargeReadAndXComplete;
SrvStartSend2( WorkContext, SrvQueueWorkToFspAtSendCompletion );
}
VOID SRVFASTCALL
SrvFsdRestartWrite (
IN OUT PWORK_CONTEXT WorkContext
)
/*++
Routine Description:
Processes file write completion for a Write SMB.
This routine is called in the FSP for a write and close SMB so that
it can free the pageable MFCB and for a write and unlock SMB so that
it can do the unlock; for other SMBs, it is called in the FSD.
Arguments:
WorkContext - Supplies a pointer to the work context block
describing server-specific context for the request.
Return Value:
None.
--*/
{
PREQ_WRITE request;
PRESP_WRITE response;
NTSTATUS status;
PRFCB rfcb;
KIRQL oldIrql;
USHORT writeLength;
UNLOCKABLE_CODE( 8FIL );
IF_DEBUG(FSD2) SrvPrint0( " - SrvFsdRestartWrite\n" );
//
// Get the request and response parameter pointers.
//
request = (PREQ_WRITE)WorkContext->RequestParameters;
response = (PRESP_WRITE)WorkContext->ResponseParameters;
//
// Get the file pointer.
//
rfcb = WorkContext->Rfcb;
IF_DEBUG(FSD2) {
SrvPrint2( " connection 0x%lx, RFCB 0x%lx\n",
WorkContext->Connection, rfcb );
}
//
// If the write failed, set an error status in the response header.
//
status = WorkContext->Irp->IoStatus.Status;
if ( !NT_SUCCESS(status) ) {
IF_DEBUG(ERRORS) SrvPrint1( "Write failed: %X\n", status );
if ( KeGetCurrentIrql() >= DISPATCH_LEVEL ) {
WorkContext->FspRestartRoutine = SrvFsdRestartWrite;
QUEUE_WORK_TO_FSP( WorkContext );
return;
}
SrvSetSmbError( WorkContext, status );
} else {
//
// The write succeeded.
//
writeLength = (USHORT)WorkContext->Irp->IoStatus.Information;
//
// Save the count of bytes written, to be used to update the
// server statistics database.
//
UPDATE_WRITE_STATS( WorkContext, writeLength );
if ( rfcb->ShareType == ShareTypeDisk ) {
//
// Update the file position.
//
rfcb->CurrentPosition = SmbGetUlong( &request->Offset ) + writeLength;
if ( WorkContext->NextCommand == SMB_COM_WRITE ) {
response->WordCount = 1;
SmbPutUshort( &response->Count, writeLength );
SmbPutUshort( &response->ByteCount, 0 );
WorkContext->ResponseParameters =
NEXT_LOCATION( response, RESP_WRITE, 0 );
//
// Processing of the SMB is complete. Send the response.
//
SrvFsdSendResponse( WorkContext );
IF_DEBUG(FSD2) SrvPrint0( "SrvFsdRestartWrite complete\n" );
return;
}
} else if ( rfcb->ShareType == ShareTypePrint ) {
//
// Update the file position.
//
if ( WorkContext->NextCommand == SMB_COM_WRITE_PRINT_FILE ) {
rfcb->CurrentPosition += writeLength;
} else {
rfcb->CurrentPosition =
SmbGetUlong( &request->Offset ) + writeLength;
}
}
//
// If this was a Write and Unlock request, do the unlock. This
// is safe because we are restarted in the FSP in this case.
//
// Note that if the write failed, the range remains locked.
//
if ( WorkContext->NextCommand == SMB_COM_WRITE_AND_UNLOCK ) {
IF_SMB_DEBUG(READ_WRITE1) {
SrvPrint0( "SrvFsdRestartWrite: unlock requested -- "
"passing request to FSP\n" );
}
SrvRestartWriteAndUnlock( WorkContext );
return;
} else if ( WorkContext->NextCommand == SMB_COM_WRITE_AND_CLOSE ) {
WorkContext->Parameters.LastWriteTime = SmbGetUlong(
&((PREQ_WRITE_AND_CLOSE)request)->LastWriteTimeInSeconds );
}
//
// If everything worked, build a response message. (If something
// failed, an error indication has already been placed in the SMB.)
//
if ( WorkContext->NextCommand == SMB_COM_WRITE_PRINT_FILE ) {
//
// ByteCount has a different offset for WRITE_PRINT_FILE
//
PRESP_WRITE_PRINT_FILE response2;
response2 = (PRESP_WRITE_PRINT_FILE)WorkContext->ResponseParameters;
response2->WordCount = 0;
SmbPutUshort( &response2->ByteCount, 0 );
WorkContext->ResponseParameters =
NEXT_LOCATION( response2, RESP_WRITE_PRINT_FILE, 0 );
} else {
response->WordCount = 1;
SmbPutUshort( &response->Count, writeLength );
SmbPutUshort( &response->ByteCount, 0 );
WorkContext->ResponseParameters =
NEXT_LOCATION( response, RESP_WRITE, 0 );
}
}
//
// If this was a Write and Close request, close the file. It is
// safe to close the RFCB here because if this is a Write and Close,
// we're actually in the FSP, not in the FSD.
//
if ( WorkContext->NextCommand == SMB_COM_WRITE_AND_CLOSE ) {
ASSERT( KeGetCurrentIrql() < DISPATCH_LEVEL );
SrvRestartChainedClose( WorkContext );
return;
}
//
// Processing of the SMB is complete. Send the response.
//
SrvFsdSendResponse( WorkContext );
IF_DEBUG(FSD2) SrvPrint0( "SrvFsdRestartWrite complete\n" );
return;
} // SrvFsdRestartWrite
VOID SRVFASTCALL
SrvFsdRestartWriteAndX (
IN OUT PWORK_CONTEXT WorkContext
)
/*++
Routine Description:
Processes file write completion for a Write and X SMB.
This routine may be called in the FSD or the FSP. If the chained
command is Close, it will be called in the FSP.
Arguments:
WorkContext - Supplies a pointer to the work context block
describing server-specific context for the request.
Return Value:
None.
--*/
{
PREQ_WRITE_ANDX request;
PREQ_NT_WRITE_ANDX ntRequest;
PRESP_WRITE_ANDX response;
NTSTATUS status;
PRFCB rfcb;
KIRQL oldIrql;
USHORT writeLength;
USHORT requestedWriteLength;
UCHAR nextCommand;
USHORT nextOffset;
USHORT reqAndXOffset;
LARGE_INTEGER position;
PREQ_CLOSE closeRequest;
UNLOCKABLE_CODE( 8FIL );
IF_DEBUG(FSD2) SrvPrint0( " - SrvFsdRestartWriteAndX\n" );
//
// Get the request and response parameter pointers.
//
request = (PREQ_WRITE_ANDX)WorkContext->RequestParameters;
ntRequest = (PREQ_NT_WRITE_ANDX)WorkContext->RequestParameters;
response = (PRESP_WRITE_ANDX)WorkContext->ResponseParameters;
//
// Get the file pointer.
//
rfcb = WorkContext->Rfcb;
IF_DEBUG(FSD2) {
SrvPrint2( " connection 0x%lx, RFCB 0x%lx\n",
WorkContext->Connection, rfcb );
}
//
// Remember where the follow-on request begins, and what the next
// command is, as we are about to overwrite this information.
//
reqAndXOffset = SmbGetUshort( &request->AndXOffset );
nextCommand = request->AndXCommand;
WorkContext->NextCommand = nextCommand;
nextOffset = SmbGetUshort( &request->AndXOffset );
//
// If the write failed, set an error status in the response header.
// We still return a valid parameter block, in case some bytes were
// written before the error occurred. Note that we do _not_ process
// the next command if the write failed.
//
// *** OS/2 server behavior. Note that this is _not_ done for core
// Write.
//
status = WorkContext->Irp->IoStatus.Status;
if ( !NT_SUCCESS(status) ) {
IF_DEBUG(ERRORS) SrvPrint1( "Write failed: %X\n", status );
if ( KeGetCurrentIrql() >= DISPATCH_LEVEL ) {
WorkContext->FspRestartRoutine = SrvFsdRestartWriteAndX;
QUEUE_WORK_TO_FSP( WorkContext );
return;
}
SrvSetSmbError( WorkContext, status );
nextCommand = SMB_COM_NO_ANDX_COMMAND;
}
//
// Update the file position.
//
writeLength = (USHORT)WorkContext->Irp->IoStatus.Information;
if ( rfcb->ShareType != ShareTypePipe ) {
//
// We will ignore the distinction between clients that supply 32-bit
// and 64-bit file offsets. The reason for doing this is because
// the only clients that will use CurrentPosition is a 32-bit file
// offset client. Therefore, the upper 32-bits will never be used
// anyway. In addition, the RFCB is per client, so there is no
// possibility of clients mixing 32-bit and 64-bit file offsets.
// Therefore, for the 64-bit client, we will only read 32-bits of file
// offset.
//
if ( request->ByteCount == 12 ) {
//
// The client supplied a 32-bit file offset.
//
rfcb->CurrentPosition = SmbGetUlong( &request->Offset ) + writeLength;
} else {
//
// The client supplied a 64-bit file offset. Only use 32-bits of
// file offset.
//
rfcb->CurrentPosition = SmbGetUlong( &ntRequest->Offset ) + writeLength;
}
}
//
// Save the count of bytes written, to be used to update the server
// statistics database.
//
UPDATE_WRITE_STATS( WorkContext, writeLength );
IF_SMB_DEBUG(READ_WRITE1) {
SrvPrint2( "SrvFsdRestartWriteAndX: Fid 0x%lx, wrote %ld bytes\n",
rfcb->Fid, writeLength );
}
//
// Build the response message.
//
requestedWriteLength = SmbGetUshort( &request->DataLength );
response->AndXCommand = nextCommand;
response->AndXReserved = 0;
SmbPutUshort(
&response->AndXOffset,
GET_ANDX_OFFSET(
WorkContext->ResponseHeader,
WorkContext->ResponseParameters,
RESP_WRITE_ANDX,
0
)
);
response->WordCount = 6;
if ( WorkContext->Parameters.Transaction == NULL ) {
SmbPutUshort( &response->Count, writeLength );
} else {
SmbPutUshort( &response->Count, requestedWriteLength );
}
SmbPutUshort( &response->Remaining, (USHORT)-1 );
SmbPutUlong( &response->Reserved, 0 );
SmbPutUshort( &response->ByteCount, 0 );
WorkContext->ResponseParameters = (PCHAR)WorkContext->ResponseHeader +
SmbGetUshort( &response->AndXOffset );
WorkContext->RequestParameters = (PUCHAR)WorkContext->RequestHeader + reqAndXOffset;
//
// If this was a raw mode write, queue the work to the FSP for
// completion. The FSP routine will handling dispatching of the
// AndX command.
//
if ( WorkContext->Parameters.Transaction != NULL ) {
WorkContext->FspRestartRoutine = SrvRestartWriteAndXRaw;
SrvQueueWorkToFsp( WorkContext );
return;
}
//
// Test for a legal followon command, and dispatch as appropriate.
// Close is handled specially.
//
switch ( nextCommand ) {
case SMB_COM_NO_ANDX_COMMAND:
//
// No more commands. Send the response.
//
SrvFsdSendResponse( WorkContext );
break;
case SMB_COM_READ:
case SMB_COM_READ_ANDX:
case SMB_COM_LOCK_AND_READ:
case SMB_COM_WRITE_ANDX:
//
// Queue the work item back to the FSP for further processing.
//
WorkContext->FspRestartRoutine = SrvRestartSmbReceived;
SrvQueueWorkToFsp( WorkContext );
break;
case SMB_COM_CLOSE:
//
// Save the last write time, to correctly set it. Call
// SrvRestartChainedClose to close the file and send the response.
//
closeRequest = (PREQ_CLOSE)
((PUCHAR)WorkContext->RequestHeader + reqAndXOffset);
WorkContext->Parameters.LastWriteTime =
closeRequest->LastWriteTimeInSeconds;
SrvRestartChainedClose( WorkContext );
break;
default: // Illegal followon command
IF_DEBUG(SMB_ERRORS) {
SrvPrint1( "SrvFsdRestartWriteAndX: Illegal followon "
"command: 0x%lx\n", nextCommand );
}
if ( KeGetCurrentIrql() >= DISPATCH_LEVEL ) {
WorkContext->Irp->IoStatus.Status = STATUS_INVALID_SMB;
WorkContext->FspRestartRoutine = SrvBuildAndSendErrorResponse;
WorkContext->FsdRestartRoutine = SrvFsdRestartSmbComplete; // after response
QUEUE_WORK_TO_FSP( WorkContext );
} else {
SrvSetSmbError( WorkContext, STATUS_INVALID_SMB );
SrvFsdSendResponse( WorkContext );
}
}
IF_DEBUG(TRACE2) SrvPrint0( "SrvFsdRestartWriteAndX complete\n" );
return;
} // SrvFsdRestartWriteAndX