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