windows-nt/Source/XPSP1/NT/inetsrv/iis/svcs/infocomm/atq/atqxmit.cxx

502 lines
13 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1996 Microsoft Corporation
Module Name :
atqxmit.cxx
Abstract:
Contains internal support routines for transmit file
Author:
Johnson Apacible (johnsona) 26-Mar-1996
--*/
#include "isatq.hxx"
#include "atqcport.hxx"
//
// local
//
VOID
I_FakeTransmitFileCompletion(
IN PVOID ClientContext,
IN DWORD BytesWritten,
IN DWORD CompletionStatus,
IN OVERLAPPED * lpo
);
VOID
I_CleanupFakeTransmitFile(
IN PATQ_CONT pAtqContext
)
{
//
// Put the old completion routine back and free allocated buffers
//
pAtqContext->arInfo.uop.opFakeXmit.CurrentState = ATQ_XMIT_NONE;
pAtqContext->pfnCompletion = pAtqContext->arInfo.uop.opFakeXmit.pfnCompletion;
pAtqContext->ClientContext = pAtqContext->arInfo.uop.opFakeXmit.ClientContext;
if ( pAtqContext->arInfo.uop.opFakeXmit.pBuffer != NULL ) {
LocalFree(pAtqContext->arInfo.uop.opFakeXmit.pBuffer);
pAtqContext->arInfo.uop.opFakeXmit.pBuffer = NULL;
}
return;
} // I_CleanupFakeTransmitFile
BOOL
SIOTransmitFile(
IN PATQ_CONT pAtqContext,
IN HANDLE hFile,
IN DWORD dwBytesInFile,
IN LPTRANSMIT_FILE_BUFFERS lpTransmitBuffers
)
/*++
Routine Description:
Posts a completion status on the completion port queue
An IO pending error code is treated as a success error code
Arguments:
patqContext - pointer to ATQ context
hFile - Handle to the file to be read.
dwBytesInFile - Number of bytes to read in the file
lpTransmitBuffers - the transmitfile structure
Return Value:
TRUE if successful, FALSE on error (call GetLastError)
--*/
{
DWORD nRead = 0;
DWORD cBuffer = 0;
ATQ_ASSERT( pAtqContext->m_pBandwidthInfo != NULL );
pAtqContext->m_pBandwidthInfo->IncTotalAllowedRequests();
//
// Store data
//
pAtqContext->pvBuff = NULL;
pAtqContext->arInfo.atqOp = AtqIoXmitFile;
pAtqContext->arInfo.lpOverlapped = &pAtqContext->Overlapped;
pAtqContext->arInfo.uop.opFakeXmit.hFile = hFile;
if( lpTransmitBuffers != NULL ) {
CopyMemory(
&pAtqContext->arInfo.uop.opFakeXmit.TransmitBuffers,
lpTransmitBuffers,
sizeof(TRANSMIT_FILE_BUFFERS)
);
} else {
ZeroMemory(
&pAtqContext->arInfo.uop.opFakeXmit.TransmitBuffers,
sizeof(pAtqContext->arInfo.uop.opFakeXmit.TransmitBuffers)
);
}
pAtqContext->arInfo.uop.opFakeXmit.CurrentState = ATQ_XMIT_START;
//
// Set current file offset to requested
//
pAtqContext->arInfo.uop.opFakeXmit.FileOffset =
pAtqContext->Overlapped.Offset;
pAtqContext->arInfo.dwLastIOError = NOERROR;
pAtqContext->arInfo.uop.opFakeXmit.pBuffer = NULL;
pAtqContext->arInfo.uop.opFakeXmit.hFile = hFile;
pAtqContext->arInfo.uop.opFakeXmit.BytesWritten = 0;
pAtqContext->arInfo.uop.opFakeXmit.pBuffer = NULL;
pAtqContext->arInfo.uop.opFakeXmit.pvLastSent = NULL;
//
// Check the number of bytes from file to send
//
if ( dwBytesInFile == 0 ) {
//
// Send the whole file.
//
dwBytesInFile = GetFileSize( hFile, NULL );
if (dwBytesInFile >= pAtqContext->Overlapped.Offset) {
dwBytesInFile -= pAtqContext->Overlapped.Offset;
} else {
ATQ_ASSERT(NULL);
dwBytesInFile = 0;
}
}
pAtqContext->arInfo.uop.opFakeXmit.BytesLeft = dwBytesInFile;
//
// replace the completion function with our own
//
pAtqContext->arInfo.uop.opFakeXmit.pfnCompletion =
pAtqContext->pfnCompletion;
pAtqContext->arInfo.uop.opFakeXmit.ClientContext =
pAtqContext->ClientContext ;
pAtqContext->ClientContext = pAtqContext;
pAtqContext->pfnCompletion = I_FakeTransmitFileCompletion;
//
// Set the timeout
//
I_SetNextTimeout(pAtqContext);
//
// Kick in transmission loop
//
I_FakeTransmitFileCompletion(pAtqContext,
0,
NO_ERROR,
&pAtqContext->Overlapped
);
SetLastError(NO_ERROR);
return(TRUE);
} // SIOTransmitFile
VOID
I_FakeTransmitFileCompletion(
IN PVOID ClientContext,
IN DWORD BytesWritten,
IN DWORD CompletionStatus,
IN OVERLAPPED * lpo
)
{
PATQ_CONT pAtqContext = (PATQ_CONT)ClientContext;
DWORD nWrite = 0;
INT err = NOERROR;
OVERLAPPED ov;
OVERLAPPED *pov = &ov;
LPVOID lpBufferSend;
BOOL fRes = FALSE;
//
// We need to use saved context value because of reasons above
//
ATQ_ASSERT(pAtqContext != NULL);
pAtqContext->arInfo.uop.opFakeXmit.BytesWritten += BytesWritten;
if ( CompletionStatus != NO_ERROR ) {
//
// An error occured, call the completion routine
//
pAtqContext->arInfo.dwLastIOError = CompletionStatus;
goto call_completion;
}
//
// Calculate pointer and number of bytes to send , depending on
// the current state of transmission
//
if (pAtqContext->arInfo.uop.opFakeXmit.CurrentState == ATQ_XMIT_START) {
//
// We are just starting transmission, check if there is any
// header part to send
//
nWrite = pAtqContext->arInfo.uop.opFakeXmit.TransmitBuffers.HeadLength;
pAtqContext->arInfo.uop.opFakeXmit.CurrentState = ATQ_XMIT_HEADR_SENT;
lpBufferSend = pAtqContext->arInfo.uop.opFakeXmit.TransmitBuffers.Head;
if ( (nWrite != 0) && (lpBufferSend != NULL) ) {
goto AtqXmitSendData;
}
}
if (pAtqContext->arInfo.uop.opFakeXmit.CurrentState == ATQ_XMIT_HEADR_SENT) {
//
// Clear written bytes counter, as this is very first iteration
//
BytesWritten = 0;
//
// Check if the file was zero lenth ?
// Check if we have temporary transmission buffer
//
if (pAtqContext->arInfo.uop.opFakeXmit.pBuffer == NULL) {
pAtqContext->arInfo.uop.opFakeXmit.pBuffer =
(PCHAR)LocalAlloc(LPTR, g_cbXmitBufferSize);
}
//
// Set starting offset for the opened file
//
SetFilePointer(
pAtqContext->arInfo.uop.opFakeXmit.hFile,
pAtqContext->arInfo.uop.opFakeXmit.FileOffset,
NULL,
FILE_BEGIN
);
pAtqContext->arInfo.uop.opFakeXmit.CurrentState =
ATQ_XMIT_TRANSMITTING_FILE;
}
if (pAtqContext->arInfo.uop.opFakeXmit.CurrentState ==
ATQ_XMIT_TRANSMITTING_FILE) {
//
// We are sending file itself - do we have anything left to do ?
//
if (pAtqContext->arInfo.uop.opFakeXmit.BytesLeft != 0) {
//
// Calculate offset for the next read .
// This would be previous offset plus number of
// bytes written on last operation.
//
pAtqContext->arInfo.uop.opFakeXmit.FileOffset += BytesWritten;
if (pAtqContext->arInfo.uop.opFakeXmit.pBuffer != NULL) {
//
// Read file from current offset
//
DWORD nToRead = g_cbXmitBufferSize;
nToRead = min(
g_cbXmitBufferSize,
pAtqContext->arInfo.uop.opFakeXmit.BytesLeft);
nWrite = 0;
fRes = ReadFile(pAtqContext->arInfo.uop.opFakeXmit.hFile,
pAtqContext->arInfo.uop.opFakeXmit.pBuffer,
nToRead,
&nWrite,
NULL);
if ( !fRes ) {
ATQ_PRINTF((DBG_CONTEXT,
"Error %d in ReadFile\n", GetLastError()));
}
if (pAtqContext->arInfo.uop.opFakeXmit.BytesLeft >= nWrite) {
pAtqContext->arInfo.uop.opFakeXmit.BytesLeft -= nWrite;
} else {
pAtqContext->arInfo.uop.opFakeXmit.BytesLeft = 0;
}
IF_DEBUG(SIO) {
ATQ_PRINTF((DBG_CONTEXT,
"[TransmitFile(%lu)] Got data from file: context=%x Offset=%x nWrite=%x fRes=%d \n",
GetCurrentThreadId(),pAtqContext,
pAtqContext->arInfo.uop.opFakeXmit.FileOffset,
nWrite,fRes));
}
//
// Read succeeded and we got the data - send it to the client
//
if (fRes && (nWrite != 0) ) {
lpBufferSend = pAtqContext->arInfo.uop.opFakeXmit.pBuffer;
goto AtqXmitSendData;
}
//
// If ReadFile failed - get error code and analyze it
//
if (!fRes) {
pAtqContext->arInfo.dwLastIOError = GetLastError();
//
// If we really shipped the whole file and error is EOF - ignore it
//
if ((pAtqContext->arInfo.dwLastIOError == ERROR_HANDLE_EOF) &&
(!pAtqContext->arInfo.uop.opFakeXmit.BytesLeft)) {
pAtqContext->arInfo.dwLastIOError = NO_ERROR;
fRes = TRUE;
}
} else {
//
// If by some reasons we did not send all data planned we need
// to report failure, causing close for socket. Otherwise client will wait
// for the rest of the data and we will wait for client read
// Nb: In some cases Read returns success but does not really get any data
// we will treat this as premature EOF
//
if (pAtqContext->arInfo.uop.opFakeXmit.BytesLeft != 0) {
pAtqContext->arInfo.dwLastIOError = ERROR_HANDLE_EOF;
fRes = FALSE;
}
}
} else {
//
// Buffer was not allocated - fail transmission
//
ATQ_PRINTF((DBG_CONTEXT,"Failed to allocate buffer\n"));
pAtqContext->arInfo.dwLastIOError = ERROR_NOT_ENOUGH_MEMORY;
}
//
// Failed read from file, terminate transmission
//
if (!fRes) {
//
// Abnormal termination of Read - reset the client socket
//
ATQ_PRINTF((DBG_CONTEXT,"Read failed. Shutdown called\n"));
shutdown( HANDLE_TO_SOCKET(pAtqContext->hAsyncIO), 1 );
goto call_completion;
}
}
//
// Free temporary buffer as we no longer need it
//
if (pAtqContext->arInfo.uop.opFakeXmit.pBuffer) {
LocalFree(pAtqContext->arInfo.uop.opFakeXmit.pBuffer);
pAtqContext->arInfo.uop.opFakeXmit.pBuffer = NULL;
}
//
// We are finished with this file
//
pAtqContext->arInfo.uop.opFakeXmit.CurrentState = ATQ_XMIT_FILE_DONE;
}
if (pAtqContext->arInfo.uop.opFakeXmit.CurrentState == ATQ_XMIT_FILE_DONE) {
//
// Check if there is any tail part to send
//
pAtqContext->arInfo.uop.opFakeXmit.CurrentState = ATQ_XMIT_TAIL_SENT;
nWrite = pAtqContext->arInfo.uop.opFakeXmit.TransmitBuffers.TailLength;
lpBufferSend = pAtqContext->arInfo.uop.opFakeXmit.TransmitBuffers.Tail;
}
AtqXmitSendData:
//
// If we have something to send - start i/o operation
//
if ( (nWrite != 0) && (lpBufferSend != NULL) ) {
pAtqContext->arInfo.atqOp = AtqIoXmitFile;
pAtqContext->arInfo.uop.opFakeXmit.pvLastSent = lpBufferSend;
pAtqContext->arInfo.uop.opFakeXmit.cbBuffer = nWrite;
InterlockedIncrement( &pAtqContext->m_nIO);
SIOStartAsyncOperation(g_hCompPort,(PATQ_CONTEXT)pAtqContext);
return ;
}
//
// If it was the last phase of transmission -
// clean up and send successful notification
//
if (pAtqContext->arInfo.uop.opFakeXmit.CurrentState != ATQ_XMIT_TAIL_SENT) {
}
pAtqContext->arInfo.dwLastIOError = NOERROR;
call_completion:
//
// Indicate no transmission in progress
//
pAtqContext->arInfo.uop.opFakeXmit.CurrentState = ATQ_XMIT_NONE;
I_CleanupFakeTransmitFile(pAtqContext);
pAtqContext->arInfo.dwTotalBytesTransferred =
pAtqContext->arInfo.uop.opFakeXmit.BytesWritten;
//
// Clean up SIO state
//
pAtqContext->dwSIOFlags &= ~ATQ_SIO_FLAG_STATE_MASK;
//
// Queue context as completed
//
SIOPostCompletionStatus(g_hCompPort,
pAtqContext->arInfo.uop.opFakeXmit.BytesWritten,
(ULONG_PTR)pAtqContext,
pAtqContext->arInfo.lpOverlapped);
return ;
} // I_FakeTransmitFileCompletion