/*++ Copyright (c) 1996 Microsoft Corporation Module Name : spud.cxx Abstract: This module implements the user mode entry points for SPUD.SYS. SPUD = Special Purpose Utility Driver. Author: John Ballard ( jballard ) 22-Oct-1996 Environment: User Mode -- Win32 Project: Internet Services Common DLL Functions Exported: BOOL AtqTransmitFileAndRecv(); BOOL AtqSendAndRecv(); BOOL AtqBatchRequest(); --*/ #include "isatq.hxx" #include #include #include #include "uspud.h" // ------------------------------ // oplock notification thread stuff // ------------------------------ #define KILL_OPLOCK_THREAD ((LPOVERLAPPED)0xffeeffee) HANDLE g_hOplockThread = NULL; DWORD WINAPI I_AtqOplockThread( IN LPVOID Param ); VOID EnableLoadDriverPrivilege( VOID ); #define SPUD_REG_PATH \ L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\Spud" BOOL I_AtqSpudInitialize( IN HANDLE hIoPort, IN HANDLE hOplockPort ) { NTSTATUS status; UNICODE_STRING DriverName; DWORD Version = SPUD_VERSION; if ( !g_fUseDriver ) { return(FALSE); } EnableLoadDriverPrivilege(); g_pfnRtlInitUnicodeString( &DriverName, SPUD_REG_PATH ); status = g_pfnNtLoadDriver( &DriverName ); if ( ( status != STATUS_SUCCESS ) && ( status != STATUS_IMAGE_ALREADY_LOADED ) ) { ATQ_PRINTF(( DBG_CONTEXT, "NtLoadDriver failed!!! status == %08lx\n", status )); goto disable_driver; } status = SPUDInitialize(Version, hIoPort, hOplockPort); if ( status != STATUS_SUCCESS ) { if ( status == STATUS_INVALID_DEVICE_REQUEST ) { SPUDTerminate(); if ( SPUDInitialize(Version, hIoPort, hOplockPort) == STATUS_SUCCESS ) { return TRUE; } } ATQ_PRINTF(( DBG_CONTEXT, "SPUDInitialize failed!!! status == %08lx\n", status )); goto disable_driver; } return TRUE; disable_driver: g_fUseDriver = FALSE; ATQ_PRINTF((DBG_CONTEXT, "SPUDInitialize: Disabling driver\n")); return(FALSE); } // I_AtqSpudInitialize BOOL I_AtqSpudTerminate() { NTSTATUS status; status = SPUDTerminate(); if ( status != STATUS_SUCCESS ) { IF_DEBUG(ERROR) { ATQ_PRINTF(( DBG_CONTEXT, "SPUDTerminate failed!!! status == %08lx\n", status )); } return FALSE; } return TRUE; } BOOL I_AtqSpudCheckStatus( IN PATQ_CONT patqContext // pointer to ATQ context ) { ULONG status; status = SPUDCheckStatus( &patqContext->spudContext ); if ( status != STATUS_SUCCESS ) { return FALSE; } return TRUE; } BOOL I_AtqTransmitFileAndRecv( IN PATQ_CONTEXT patqContext, // pointer to ATQ context IN HANDLE hFile, // handle of file to read IN DWORD dwBytesInFile, // Bytes to transmit IN LPTRANSMIT_FILE_BUFFERS lpTransmitBuffers, // transmit buffer structure IN DWORD dwTFFlags, // TF Flags IN LPWSABUF pwsaBuffers, // Buffers for recv IN DWORD dwBufferCount ) /*++ Routine Description: Calls SPUDTransmitFileAndRecv(). Cannot be blocked by bandwidth throttler Return Value: TRUE if successful, FALSE on error (call GetLastError) --*/ { ULONG status; AFD_TRANSMIT_FILE_INFO transmitInfo; AFD_RECV_INFO recvInfo; PATQ_CONT patqCont = (PATQ_CONT)patqContext; IF_DEBUG(API_ENTRY) { ATQ_PRINTF(( DBG_CONTEXT, "I_AtqTransmitFileAndRecv(%08lx) called.\n", patqContext)); } transmitInfo.WriteLength.QuadPart = dwBytesInFile; transmitInfo.SendPacketLength = 0; transmitInfo.FileHandle = hFile; transmitInfo.Flags = dwTFFlags; if ( lpTransmitBuffers != NULL ) { transmitInfo.Head = lpTransmitBuffers->Head; transmitInfo.HeadLength = lpTransmitBuffers->HeadLength; transmitInfo.Tail = lpTransmitBuffers->Tail; transmitInfo.TailLength = lpTransmitBuffers->TailLength; } else { transmitInfo.Head = NULL; transmitInfo.HeadLength = 0; transmitInfo.Tail = NULL; transmitInfo.TailLength = 0; } transmitInfo.Offset.LowPart = patqContext->Overlapped.Offset; transmitInfo.Offset.HighPart = 0; recvInfo.BufferArray = pwsaBuffers; recvInfo.BufferCount = dwBufferCount; recvInfo.AfdFlags = AFD_OVERLAPPED; recvInfo.TdiFlags = TDI_RECEIVE_NORMAL; patqCont->ResetFlag( ACF_RECV_CALLED); status = SPUDTransmitFileAndRecv( patqCont->hAsyncIO, &transmitInfo, &recvInfo, &patqCont->spudContext ); if ( status != STATUS_SUCCESS && status != STATUS_PENDING ) { ATQ_PRINTF(( DBG_CONTEXT, "SPUDTransmitFileAndRecv failed!!! status == %08lx\n", status )); SetLastError(g_pfnRtlNtStatusToDosError(status)); patqCont->MoveState( ACS_SOCK_CONNECTED); return FALSE; } patqCont->SetFlag( ACF_RECV_ISSUED); return TRUE; } BOOL AtqTransmitFileAndRecv( IN PATQ_CONTEXT patqContext, // pointer to ATQ context IN HANDLE hFile, // handle of file to read IN DWORD dwBytesInFile, // Bytes to transmit IN LPTRANSMIT_FILE_BUFFERS lpTransmitBuffers, // transmit buffer structure IN DWORD dwTFFlags, // TF Flags IN LPWSABUF pwsaBuffers, // Buffers for recv IN DWORD dwBufferCount ) { BOOL fRes; PATQ_CONT pContext = (PATQ_CONT) patqContext; PBANDWIDTH_INFO pBandwidthInfo = pContext->m_pBandwidthInfo; ATQ_ASSERT( pContext->Signature == ATQ_CONTEXT_SIGNATURE ); ATQ_ASSERT( pContext->arInfo.atqOp == AtqIoNone); ATQ_ASSERT( pBandwidthInfo != NULL ); ATQ_ASSERT( pBandwidthInfo->QuerySignature() == ATQ_BW_INFO_SIGNATURE ); if ( !g_fUseDriver || pContext->IsFlag( ACF_RECV_ISSUED) ) { BOOL fRes; IF_DEBUG(API_ENTRY) { ATQ_PRINTF(( DBG_CONTEXT, "AtqTransmitFileAndRecv(%08lx) g_fUseDriver == FALSE\n Calling AtqTransmitFile.\n", patqContext)); } return AtqTransmitFile( patqContext, hFile, dwBytesInFile, lpTransmitBuffers, dwTFFlags ); } I_SetNextTimeout(pContext); pContext->BytesSent = dwBytesInFile; DBG_ASSERT( dwBufferCount >= 1); pContext->BytesSent += pwsaBuffers->len; if ( dwBufferCount > 1) { LPWSABUF pWsaBuf; for ( pWsaBuf = pwsaBuffers + 1; pWsaBuf <= (pwsaBuffers + dwBufferCount); pWsaBuf++) { pContext->BytesSent += pWsaBuf->len; } } if ( dwTFFlags == 0 ) { // // If no flags are set, then we can attempt to use the special // write-behind flag. This flag can cause the TransmitFile to // complete immediately, before the send actually completes. // This can be a significant performance improvement inside the // system. // dwTFFlags = TF_WRITE_BEHIND; } InterlockedIncrement( &pContext->m_nIO); switch ( pBandwidthInfo->QueryStatus( AtqIoXmitFileRecv ) ) { case StatusAllowOperation: pBandwidthInfo->IncTotalAllowedRequests(); fRes = I_AtqTransmitFileAndRecv( patqContext, hFile, dwBytesInFile, lpTransmitBuffers, dwTFFlags, pwsaBuffers, dwBufferCount ) || (GetLastError() == ERROR_IO_PENDING); if (!fRes) { InterlockedDecrement( &pContext->m_nIO); }; break; case StatusBlockOperation: // store data for restarting the operation. pContext->arInfo.atqOp = AtqIoXmitFileRecv; pContext->arInfo.lpOverlapped = &pContext->Overlapped; pContext->arInfo.uop.opXmitRecv.hFile = hFile; pContext->arInfo.uop.opXmitRecv.dwBytesInFile = dwBytesInFile; pContext->arInfo.uop.opXmitRecv.lpXmitBuffers = lpTransmitBuffers; pContext->arInfo.uop.opXmitRecv.dwTFFlags = dwTFFlags; pContext->arInfo.uop.opXmitRecv.dwBufferCount = dwBufferCount; if ( dwBufferCount == 1) { pContext->arInfo.uop.opXmitRecv.buf1.len = pwsaBuffers->len; pContext->arInfo.uop.opXmitRecv.buf1.buf = pwsaBuffers->buf; pContext->arInfo.uop.opXmitRecv.pBufAll = NULL; } else { DBG_ASSERT( dwBufferCount > 1); WSABUF * pBuf = (WSABUF *) ::LocalAlloc( LPTR, dwBufferCount * sizeof (WSABUF)); if ( NULL != pBuf) { pContext->arInfo.uop.opXmitRecv.pBufAll = pBuf; CopyMemory( pBuf, pwsaBuffers, dwBufferCount * sizeof(WSABUF)); } else { return ( FALSE); } } // Put this request in queue of blocked requests. fRes = pBandwidthInfo->BlockRequest( pContext); if ( fRes ) { pBandwidthInfo->IncTotalBlockedRequests(); break; } // fall through case StatusRejectOperation: InterlockedDecrement( &pContext->m_nIO); pBandwidthInfo->IncTotalRejectedRequests(); SetLastError( ERROR_NETWORK_BUSY); fRes = FALSE; break; default: ATQ_ASSERT( FALSE); SetLastError( ERROR_INVALID_PARAMETER); fRes = FALSE; break; } // switch() return fRes; } // AtqTransmitFileAndRecv() BOOL I_AtqSendAndRecv( IN PATQ_CONTEXT patqContext, // pointer to ATQ context IN LPWSABUF pwsaSendBuffers, // buffers for send IN DWORD dwSendBufferCount, // count of buffers for send IN LPWSABUF pwsaRecvBuffers, // Buffers for recv IN DWORD dwRecvBufferCount // count of buffers for recv ) /*++ Routine Description: Calls SPUDSendAndRecv(). Cannot be blocked by bandwidth throttler. Return Value: TRUE if successful, FALSE on error (call GetLastError) --*/ { ULONG status; AFD_SEND_INFO sendInfo; AFD_RECV_INFO recvInfo; PATQ_CONT patqCont = (PATQ_CONT)patqContext; IF_DEBUG(API_ENTRY) { ATQ_PRINTF(( DBG_CONTEXT, "I_AtqSendAndRecv(%08lx) called.\n", patqContext)); } sendInfo.BufferArray = pwsaSendBuffers; sendInfo.BufferCount = dwSendBufferCount; sendInfo.AfdFlags = AFD_OVERLAPPED; sendInfo.TdiFlags = 0; recvInfo.BufferArray = pwsaRecvBuffers; recvInfo.BufferCount = dwRecvBufferCount; recvInfo.AfdFlags = AFD_OVERLAPPED; recvInfo.TdiFlags = TDI_RECEIVE_NORMAL; patqCont->ResetFlag( ACF_RECV_CALLED); status = SPUDSendAndRecv( patqCont->hAsyncIO, &sendInfo, &recvInfo, &patqCont->spudContext ); if ( status != STATUS_SUCCESS && status != STATUS_PENDING ) { ATQ_PRINTF(( DBG_CONTEXT, "SPUDSendAndRecv failed!!! status == %08lx\n", status )); SetLastError(g_pfnRtlNtStatusToDosError(status)); return FALSE; } patqCont->SetFlag( ACF_RECV_ISSUED); return TRUE; } BOOL AtqSendAndRecv( IN PATQ_CONTEXT patqContext, // pointer to ATQ context IN LPWSABUF pwsaSendBuffers, // buffers for send IN DWORD dwSendBufferCount, // count of buffers for send IN LPWSABUF pwsaRecvBuffers, // Buffers for recv IN DWORD dwRecvBufferCount // count of buffers for recv ) { BOOL fRes; PATQ_CONT pContext = (PATQ_CONT) patqContext; PBANDWIDTH_INFO pBandwidthInfo = pContext->m_pBandwidthInfo; ATQ_ASSERT( pContext->Signature == ATQ_CONTEXT_SIGNATURE ); ATQ_ASSERT( pContext->arInfo.atqOp == AtqIoNone); ATQ_ASSERT( pBandwidthInfo != NULL ); ATQ_ASSERT( pBandwidthInfo->QuerySignature() == ATQ_BW_INFO_SIGNATURE ); IF_DEBUG(API_ENTRY) { ATQ_PRINTF(( DBG_CONTEXT, "AtqSendAndRecv(%08lx) called.\n", patqContext)); } if ( !g_fUseDriver || pContext->IsFlag( ACF_RECV_ISSUED) ) { BOOL fRes; DWORD cbWritten; IF_DEBUG(API_ENTRY) { ATQ_PRINTF(( DBG_CONTEXT, "AtqSendAndRecv(%08lx) g_fUseDriver == FALSE\n Calling AtqWriteSocket.\n", patqContext)); } return AtqWriteSocket( patqContext, pwsaSendBuffers, dwSendBufferCount, &patqContext->Overlapped ); } InterlockedIncrement( &pContext->m_nIO); I_SetNextTimeout(pContext); // // count the number of bytes // DBG_ASSERT( dwSendBufferCount >= 1); pContext->BytesSent = pwsaSendBuffers->len; if ( dwSendBufferCount > 1) { LPWSABUF pWsaBuf; for ( pWsaBuf = pwsaSendBuffers + 1; pWsaBuf <= (pwsaSendBuffers + dwSendBufferCount); pWsaBuf++) { pContext->BytesSent += pWsaBuf->len; } } DBG_ASSERT( dwRecvBufferCount >= 1); pContext->BytesSent += pwsaRecvBuffers->len; if ( dwRecvBufferCount > 1) { LPWSABUF pWsaBuf; for ( pWsaBuf = pwsaRecvBuffers + 1; pWsaBuf <= (pwsaRecvBuffers + dwRecvBufferCount); pWsaBuf++) { pContext->BytesSent += pWsaBuf->len; } } switch ( pBandwidthInfo->QueryStatus( AtqIoSendRecv ) ) { case StatusAllowOperation: pBandwidthInfo->IncTotalAllowedRequests(); fRes = I_AtqSendAndRecv( patqContext, pwsaSendBuffers, dwSendBufferCount, pwsaRecvBuffers, dwRecvBufferCount ) || (GetLastError() == ERROR_IO_PENDING); if (!fRes) { InterlockedDecrement( &pContext->m_nIO); }; break; case StatusBlockOperation: // store data for restarting the operation. pContext->arInfo.atqOp = AtqIoSendRecv; pContext->arInfo.lpOverlapped = &pContext->Overlapped; pContext->arInfo.uop.opSendRecv.dwSendBufferCount = dwSendBufferCount; pContext->arInfo.uop.opSendRecv.dwRecvBufferCount = dwRecvBufferCount; if ( dwSendBufferCount == 1) { pContext->arInfo.uop.opSendRecv.sendbuf1.len = pwsaSendBuffers->len; pContext->arInfo.uop.opSendRecv.sendbuf1.buf = pwsaSendBuffers->buf; pContext->arInfo.uop.opSendRecv.pSendBufAll = NULL; } else { DBG_ASSERT( dwSendBufferCount > 1); WSABUF * pBuf = (WSABUF *) ::LocalAlloc( LPTR, dwSendBufferCount * sizeof (WSABUF)); if ( NULL != pBuf) { pContext->arInfo.uop.opSendRecv.pSendBufAll = pBuf; CopyMemory( pBuf, pwsaSendBuffers, dwSendBufferCount * sizeof(WSABUF)); } else { return ( FALSE); } } if ( dwRecvBufferCount == 1) { pContext->arInfo.uop.opSendRecv.recvbuf1.len = pwsaRecvBuffers->len; pContext->arInfo.uop.opSendRecv.recvbuf1.buf = pwsaRecvBuffers->buf; pContext->arInfo.uop.opSendRecv.pRecvBufAll = NULL; } else { DBG_ASSERT( dwRecvBufferCount > 1); WSABUF * pBuf = (WSABUF *) ::LocalAlloc( LPTR, dwRecvBufferCount * sizeof (WSABUF)); if ( NULL != pBuf) { pContext->arInfo.uop.opSendRecv.pRecvBufAll = pBuf; CopyMemory( pBuf, pwsaRecvBuffers, dwRecvBufferCount * sizeof(WSABUF)); } else { return ( FALSE); } } // Put this request in queue of blocked requests. fRes = pBandwidthInfo->BlockRequest( pContext); if ( fRes ) { pBandwidthInfo->IncTotalBlockedRequests(); break; } // fall through case StatusRejectOperation: InterlockedDecrement( &pContext->m_nIO); pBandwidthInfo->IncTotalRejectedRequests(); SetLastError( ERROR_NETWORK_BUSY); fRes = FALSE; break; default: ATQ_ASSERT( FALSE); SetLastError( ERROR_INVALID_PARAMETER); fRes = FALSE; break; } // switch() return fRes; } // AtqSendAndRecv() // // Short routine to enable the LoadDriverPrivilege for loading spud.sys // VOID EnableLoadDriverPrivilege( VOID ) { HANDLE ProcessHandle; HANDLE TokenHandle; BOOL Result; LUID LoadDriverValue; TOKEN_PRIVILEGES * TokenPrivileges; CHAR buf[ 5 * sizeof(TOKEN_PRIVILEGES) ]; ProcessHandle = OpenProcess( PROCESS_QUERY_INFORMATION, FALSE, GetCurrentProcessId() ); if ( ProcessHandle == NULL ) { // // This should not happen // goto Cleanup; } Result = OpenProcessToken ( ProcessHandle, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &TokenHandle ); if ( !Result ) { // // This should not happen // goto Cleanup; } // // Find out the value of LoadDriverPrivilege // Result = LookupPrivilegeValue( NULL, "SeLoadDriverPrivilege", &LoadDriverValue ); if ( !Result ) { goto Cleanup; } // // Set up the privilege set we will need // TokenPrivileges = (TOKEN_PRIVILEGES *) buf; TokenPrivileges->PrivilegeCount = 1; TokenPrivileges->Privileges[0].Luid = LoadDriverValue; TokenPrivileges->Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; (VOID) AdjustTokenPrivileges ( TokenHandle, FALSE, TokenPrivileges, sizeof(buf), NULL, NULL ); Cleanup: if ( TokenHandle ) { CloseHandle( TokenHandle ); } if ( ProcessHandle ) { CloseHandle( ProcessHandle ); } } HANDLE AtqCreateFileW( LPCWSTR lpFileName, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwFlagsAndAttributes, SECURITY_INFORMATION si, PSECURITY_DESCRIPTOR sd, ULONG Length, PULONG LengthNeeded, ATQ_OPLOCK_COMPLETION pfnOplockCompletion, PVOID Context ) { NTSTATUS Status; OBJECT_ATTRIBUTES Obja; HANDLE Handle; UNICODE_STRING FileName; IO_STATUS_BLOCK IoStatusBlock; BOOLEAN TranslationStatus; RTL_RELATIVE_NAME_U RelativeName; PVOID FreeBuffer; ULONG CreateFlags; DWORD SQOSFlags; SECURITY_QUALITY_OF_SERVICE SecurityQualityOfService; POPLOCK_INFO pOplock; CreateFlags = 0; // DbgPrint("AtqCreateFileW - %ws\n", lpFileName ); TranslationStatus = g_pfnRtlDosPathNameToNtPathName_U( lpFileName, &FileName, NULL, &RelativeName ); if ( !TranslationStatus ) { SetLastError(ERROR_PATH_NOT_FOUND); return INVALID_HANDLE_VALUE; } FreeBuffer = FileName.Buffer; if ( RelativeName.RelativeName.Length ) { FileName = RelativeName.RelativeName; } else { RelativeName.ContainingDirectory = NULL; } InitializeObjectAttributes( &Obja, &FileName, (dwFlagsAndAttributes & FILE_FLAG_POSIX_SEMANTICS) ? 0 : OBJ_CASE_INSENSITIVE, RelativeName.ContainingDirectory, NULL ); if ( ARGUMENT_PRESENT(lpSecurityAttributes) ) { Obja.SecurityDescriptor = lpSecurityAttributes->lpSecurityDescriptor; if ( lpSecurityAttributes->bInheritHandle ) { Obja.Attributes |= OBJ_INHERIT; } } CreateFlags |= (dwFlagsAndAttributes & FILE_FLAG_NO_BUFFERING ? FILE_NO_INTERMEDIATE_BUFFERING : 0 ); CreateFlags |= (dwFlagsAndAttributes & FILE_FLAG_WRITE_THROUGH ? FILE_WRITE_THROUGH : 0 ); CreateFlags |= (dwFlagsAndAttributes & FILE_FLAG_OVERLAPPED ? 0 : FILE_SYNCHRONOUS_IO_NONALERT ); CreateFlags |= (dwFlagsAndAttributes & FILE_FLAG_SEQUENTIAL_SCAN ? FILE_SEQUENTIAL_ONLY : 0 ); CreateFlags |= (dwFlagsAndAttributes & FILE_FLAG_RANDOM_ACCESS ? FILE_RANDOM_ACCESS : 0 ); CreateFlags |= (dwFlagsAndAttributes & FILE_FLAG_BACKUP_SEMANTICS ? FILE_OPEN_FOR_BACKUP_INTENT : 0 ); if ( pfnOplockCompletion != NULL ) { pOplock = (POPLOCK_INFO) ::LocalAlloc( LPTR, sizeof(OPLOCK_INFO)); if ( pOplock ) { pOplock->pfnOplockCompletion = pfnOplockCompletion; pOplock->Context = Context; } } else { pOplock = NULL; } Status = SPUDCreateFile( &Handle, &Obja, &IoStatusBlock, dwFlagsAndAttributes & (FILE_ATTRIBUTE_VALID_FLAGS & ~FILE_ATTRIBUTE_DIRECTORY), dwShareMode, CreateFlags, si, sd, Length, LengthNeeded, pOplock ); g_pfnRtlFreeHeap(RtlProcessHeap(), 0,FreeBuffer); if ( !NT_SUCCESS(Status) ) { if ( Status == STATUS_INVALID_PARAMETER || Status == STATUS_OPLOCK_NOT_GRANTED ) { SetLastError(ERROR_INVALID_ACCESS); return Handle; } if ( Status == STATUS_FILE_IS_A_DIRECTORY ) { SetLastError(ERROR_ACCESS_DENIED); } else { SetLastError(g_pfnRtlNtStatusToDosError(Status)); } return INVALID_HANDLE_VALUE; } SetLastError(ERROR_SUCCESS); return Handle; } BOOL AtqOplockAcknowledge( IN HANDLE FileHandle, ATQ_OPLOCK_COMPLETION pfnOplockCompletion, PVOID Context ) { POPLOCK_INFO pOplock; NTSTATUS Status; if ( pfnOplockCompletion != NULL ) { pOplock = (POPLOCK_INFO) ::LocalAlloc( LPTR, sizeof(OPLOCK_INFO)); if ( pOplock ) { pOplock->pfnOplockCompletion = pfnOplockCompletion; pOplock->Context = Context; Status = SPUDOplockAcknowledge( FileHandle, pOplock ); if ( Status == STATUS_SUCCESS ) { return TRUE; } } } return FALSE; } BOOL AtqSpudInitialized( VOID ) { return g_fUseDriver; } BOOL I_AtqStartOplockThread( VOID ) { // // Sanity check. // ATQ_ASSERT( g_fUseDriver ); ATQ_ASSERT( g_hOplockCompPort != NULL ); ATQ_ASSERT( g_hOplockThread == NULL ); // // Create the thread. This thread will receive all oplock break // notifications from SPUD. // g_hOplockThread = CreateThread( NULL, // lpThreadAttributes 0, // dwStackSize &I_AtqOplockThread, // lpStartAddress, NULL, // lpParameter 0, // dwCreationFlags NULL // lpThreadId ); if( g_hOplockThread == NULL ) { return FALSE; } return TRUE; } // I_AtqStartOplockThread VOID I_AtqKillOplockThread( VOID ) { BOOL result; // // Sanity check. // ATQ_ASSERT( g_fUseDriver ); ATQ_ASSERT( g_hOplockCompPort != NULL ); ATQ_ASSERT( g_hOplockThread != NULL ); // // Post a special "kill yourself" notification to the oplock thread. // result = g_pfnPostCompletionStatus( g_hOplockCompPort, 0, 0, KILL_OPLOCK_THREAD ); if( result ) { // // Wait for the thread to die. // WaitForSingleObject( g_hOplockThread, INFINITE ); CloseHandle( g_hOplockThread ); g_hOplockThread = NULL; } else { ATQ_ASSERT( !"PostCompletionStatus() failed" ); } } // I_AtqKillOplockThread DWORD WINAPI I_AtqOplockThread( IN LPVOID Param ) { BOOL result; DWORD oplockStatus; LPOVERLAPPED overlapped; POPLOCK_INFO oplockInfo; OPLOCK_INFO capturedInfo; // // Sanity check. // ATQ_ASSERT( g_fUseDriver ); ATQ_ASSERT( g_hOplockCompPort != NULL ); ATQ_ASSERT( g_hOplockThread != NULL ); // // Spin forever, or at least until we're shut down. // while( TRUE ) { // // Get the next entry from the queue. // result = g_pfnGetQueuedCompletionStatus( g_hOplockCompPort, &oplockStatus, (LPDWORD)&oplockInfo, &overlapped, INFINITE ); if( result ) { // // Check for the special "kill yourself" notification. // if( overlapped == KILL_OPLOCK_THREAD ) { break; } // // Validate the return values. // ATQ_ASSERT( overlapped == NULL ); ATQ_ASSERT( oplockInfo != NULL ); ATQ_ASSERT( oplockStatus == OPLOCK_BREAK_NO_OPLOCK || oplockStatus == OPLOCK_BREAK_OPEN || oplockStatus == OPLOCK_BREAK_CLOSE ); // // Capture the oplock info, then free the structure. // capturedInfo = *oplockInfo; LocalFree( oplockInfo ); // // Call the oplock completion routine as specified in the // returned info structure. // (capturedInfo.pfnOplockCompletion)( capturedInfo.Context, oplockStatus ); } else { // // GetQueuedCompletionStatus() failed. Bail. // DBGPRINTF(( DBG_CONTEXT, "I_AtqOplockThread: GetQueuedCompletionStatus() failed, error %lu\n", GetLastError() )); ATQ_ASSERT( !"GetQueuedCompletionStatus() failed" ); break; } } return 0; } // I_AtqOplockThread