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