502 lines
14 KiB
C++
502 lines
14 KiB
C++
|
/*++
|
|||
|
|
|||
|
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
|
|||
|
|