/*++ Copyright (c) 1989 Microsoft Corporation Module Name: smbclose.c Abstract: This module contains routines for processing the following SMBs: Close Author: David Treadwell (davidtr) 16-Nov-1989 Revision History: --*/ #include "precomp.h" #include "smbclose.tmh" #pragma hdrstop #ifdef ALLOC_PRAGMA #pragma alloc_text( PAGE, SrvSmbClose ) #endif SMB_PROCESSOR_RETURN_TYPE SrvSmbClose ( SMB_PROCESSOR_PARAMETERS ) /*++ Routine Description: Processes a Close SMB. Arguments: SMB_PROCESSOR_PARAMETERS - See smbprocs.h for a description of the parameters to SMB processor routines. Return Value: SMB_PROCESSOR_RETURN_TYPE - See smbprocs.h --*/ { PREQ_CLOSE request; PRESP_CLOSE response; NTSTATUS status = STATUS_SUCCESS; #ifdef INCLUDE_SMB_IFMODIFIED BOOLEAN extendedInfo = FALSE; SRV_NETWORK_OPEN_INFORMATION fileNetInfo; ULONG flags; USN usnValue; ULONGLONG fileRefNumber; #endif PSESSION session; PRFCB rfcb; SMB_STATUS SmbStatus = SmbStatusInProgress; PAGED_CODE( ); if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT) WorkContext->PreviousSMB = EVENT_TYPE_SMB_CLOSE; SrvWmiStartContext(WorkContext); IF_SMB_DEBUG(OPEN_CLOSE1) { KdPrint(( "Close file request header at 0x%p, response header at 0x%p\n", WorkContext->RequestHeader, WorkContext->ResponseHeader )); KdPrint(( "Close file request parameters at 0x%p, response parameters at 0x%p\n", WorkContext->RequestParameters, WorkContext->ResponseParameters )); } // // Set up parameters. // request = (PREQ_CLOSE)(WorkContext->RequestParameters); response = (PRESP_CLOSE)(WorkContext->ResponseParameters); // // If a session block has not already been assigned to the current // work context, verify the UID. If verified, the address of the // session block corresponding to this user is stored in the // WorkContext block and the session block is referenced. // session = SrvVerifyUid( WorkContext, SmbGetAlignedUshort( &WorkContext->RequestHeader->Uid ) ); if ( session == NULL ) { IF_DEBUG(SMB_ERRORS) { KdPrint(( "SrvSmbClose: Invalid UID: 0x%lx\n", SmbGetAlignedUshort( &WorkContext->RequestHeader->Uid ) )); } SrvSetSmbError( WorkContext, STATUS_SMB_BAD_UID ); SmbStatus = SmbStatusSendResponse; goto Cleanup; } // // First, verify the FID. If verified, the RFCB and the TreeConnect // block are referenced and their addresses are stored in the // WorkContext block, and the RFCB address is returned. // // Call SrvVerifyFid, but do not fail (return NULL) if there // is a saved write behind error for this rfcb. The rfcb is // needed in order to process the close. // rfcb = SrvVerifyFid( WorkContext, SmbGetUshort( &request->Fid ), FALSE, SrvRestartSmbReceived, // serialize with raw write &status ); if ( rfcb == SRV_INVALID_RFCB_POINTER ) { if ( !NT_SUCCESS( status ) ) { // // Invalid file ID. Reject the request. // IF_DEBUG(SMB_ERRORS) { KdPrint(( "SrvSmbClose: Invalid FID: 0x%lx\n", SmbGetUshort( &request->Fid ) )); } SrvSetSmbError( WorkContext, STATUS_INVALID_HANDLE ); SmbStatus = SmbStatusSendResponse; goto Cleanup; } // // The work item has been queued because a raw write is in // progress. // SmbStatus = SmbStatusInProgress; goto Cleanup; } else if( rfcb->ShareType == ShareTypePrint && WorkContext->UsingBlockingThread == 0 ) { // // Closing this file will result in the scheduling of a print // job. This means we will have to talk with srvsvc, a lengthy // operation. Shift this close over to a blocking thread. // SrvQueueWorkToBlockingThread( WorkContext ); SmbStatus = SmbStatusInProgress; goto Cleanup; } else if ( !NT_SUCCESS( rfcb->SavedError ) ) { // // Check the saved error. // (VOID) SrvCheckForSavedError( WorkContext, rfcb ); } // // Set the last write time on the file from the time specified in // the SMB. Even though the SMB spec says that this is optional, // we must support it for the following reasons: // // 1) The only way to set a file time in DOS is through a // handle-based API which the DOS redir never sees; the API // just sets the time in DOS's FCB, and the redir is expected // set the time when it closes the file. Therefore, if we // didn't do this, there would be no way t set a file time // from DOS. // // 2) It is better for a file to have a redirector's version // of a time than the server's. This keeps the time // consistent for apps running on the client. Setting // the file time on close keeps the file time consistent // with the time on the client. // // !!! should we do anything with the return code from this routine? if( rfcb->WriteAccessGranted || #ifdef INCLUDE_SMB_IFMODIFIED rfcb->WrittenTo || #endif rfcb->AppendAccessGranted ) { #ifdef INCLUDE_SMB_IFMODIFIED (VOID)SrvSetLastWriteTime( rfcb, SmbGetUlong( &request->LastWriteTimeInSeconds ), rfcb->GrantedAccess, TRUE ); #else (VOID)SrvSetLastWriteTime( rfcb, SmbGetUlong( &request->LastWriteTimeInSeconds ), rfcb->GrantedAccess ); #endif } // // Now proceed to do the actual close file, even if there was // a write behind error. // #ifdef SLMDBG if ( SrvIsSlmStatus( &rfcb->Mfcb->FileName ) && (rfcb->GrantedAccess & FILE_WRITE_DATA) ) { ULONG offset; status = SrvValidateSlmStatus( rfcb->Lfcb->FileHandle, WorkContext, &offset ); if ( !NT_SUCCESS(status) ) { SrvReportCorruptSlmStatus( &rfcb->Mfcb->FileName, status, offset, SLMDBG_CLOSE, rfcb->Lfcb->Session ); SrvReportSlmStatusOperations( rfcb, FALSE ); SrvDisallowSlmAccess( &rfcb->Lfcb->FileObject->FileName, rfcb->Lfcb->TreeConnect->Share->RootDirectoryHandle ); SrvSetSmbError( WorkContext, STATUS_DISK_CORRUPT_ERROR ); } } #endif #ifdef INCLUDE_SMB_IFMODIFIED if (request->WordCount == 5 && rfcb->ShareType == ShareTypeDisk) { // // This is an extended close request, fill in all the new fields // status = SrvQueryNetworkOpenInformation( rfcb->Lfcb->FileHandle, rfcb->Lfcb->FileObject, &fileNetInfo, FALSE ); if ( NT_SUCCESS(status) ) { PREQ_EXTENDED_CLOSE extendedRequest = (PREQ_EXTENDED_CLOSE) request; LARGE_INTEGER ourUsnValue; LARGE_INTEGER ourFileRefNumber; BOOLEAN writeClose; flags = SmbGetUlong( &extendedRequest->Flags ); extendedInfo = TRUE; usnValue = 0; fileRefNumber = 0; if (rfcb->Lfcb->FileUpdated) { // // the file has been updated, let's close out the current // usn journal entry so that we can get an accurate USN // number (rather than have the entry generated at close). // rfcb->Lfcb->FileUpdated = FALSE; writeClose = TRUE; } else { writeClose = FALSE; } // // get the current USN number for this file. // status = SrvIssueQueryUsnInfoRequest( rfcb, writeClose, &ourUsnValue, &ourFileRefNumber ); if (NT_SUCCESS(status)) { usnValue = ourUsnValue.QuadPart; fileRefNumber = ourFileRefNumber.QuadPart; } else { IF_DEBUG(ERRORS) { KdPrint(( "SrvSmbClose: Query USN info failed: 0x%X for handle %u\n", status, rfcb->Lfcb->FileObject )); } } } else { IF_DEBUG(SMB_ERRORS) { KdPrint(( "SrvSmbClose: NtGetFileInfo returned 0x%lx\n", status )); } } } #endif SrvCloseRfcb( rfcb ); // // Dereference the RFCB immediately, rather than waiting for normal // work context cleanup after the response send completes. This // gets the xFCB structures cleaned up in a more timely manner. // // *** The specific motivation for this change was to fix a problem // where a compatibility mode open was closed, the response was // sent, and a Delete SMB was received before the send // completion was processed. This resulted in the MFCB and LFCB // still being present, which caused the delete processing to // try to use the file handle in the LFCB, which we just closed // here. // SrvDereferenceRfcb( rfcb ); WorkContext->Rfcb = NULL; WorkContext->OplockOpen = FALSE; #if 0 // // If this is a CloseAndTreeDisc SMB, do the tree disconnect. // if ( WorkContext->RequestHeader->Command == SMB_COM_CLOSE_AND_TREE_DISC ) { IF_SMB_DEBUG(OPEN_CLOSE1) { KdPrint(( "Disconnecting tree 0x%lx\n", WorkContext->TreeConnect )); } SrvCloseTreeConnect( WorkContext->TreeConnect ); } #endif // // Build the response SMB. // #ifdef INCLUDE_SMB_IFMODIFIED if ( ! extendedInfo ) { #endif response->WordCount = 0; SmbPutUshort( &response->ByteCount, 0 ); WorkContext->ResponseParameters = NEXT_LOCATION( response, RESP_CLOSE, 0 ); #ifdef INCLUDE_SMB_IFMODIFIED } else { PRESP_EXTENDED_CLOSE extendedResponse = (PRESP_EXTENDED_CLOSE) response; LARGE_INTEGER usnToLarge; extendedResponse->WordCount = SMB_RESP_EXTENDED_CLOSE_WORK_COUNT; SmbPutUshort( &extendedResponse->ByteCount, 0 ); SmbPutUlong( &extendedResponse->Flags, flags ); SmbPutUlong( &extendedResponse->CreationTime.HighPart, fileNetInfo.CreationTime.HighPart ); SmbPutUlong( &extendedResponse->CreationTime.LowPart, fileNetInfo.CreationTime.LowPart ); SmbPutUlong( &extendedResponse->LastWriteTime.HighPart, fileNetInfo.LastWriteTime.HighPart ); SmbPutUlong( &extendedResponse->LastWriteTime.LowPart, fileNetInfo.LastWriteTime.LowPart ); SmbPutUlong( &extendedResponse->ChangeTime.HighPart, fileNetInfo.ChangeTime.HighPart ); SmbPutUlong( &extendedResponse->ChangeTime.LowPart, fileNetInfo.ChangeTime.LowPart ); SmbPutUlong( &extendedResponse->AllocationSize.HighPart, fileNetInfo.AllocationSize.HighPart ); SmbPutUlong( &extendedResponse->AllocationSize.LowPart, fileNetInfo.AllocationSize.LowPart ); SmbPutUlong( &extendedResponse->EndOfFile.HighPart, fileNetInfo.EndOfFile.HighPart ); SmbPutUlong( &extendedResponse->EndOfFile.LowPart, fileNetInfo.EndOfFile.LowPart ); usnToLarge.QuadPart = usnValue; SmbPutUlong( &extendedResponse->UsnValue.HighPart, usnToLarge.HighPart ); SmbPutUlong( &extendedResponse->UsnValue.LowPart, usnToLarge.LowPart ); usnToLarge.QuadPart = fileRefNumber; SmbPutUlong( &extendedResponse->FileReferenceNumber.HighPart, usnToLarge.HighPart ); SmbPutUlong( &extendedResponse->FileReferenceNumber.LowPart, usnToLarge.LowPart ); SmbPutUlong( &extendedResponse->FileAttributes, fileNetInfo.FileAttributes ); WorkContext->ResponseParameters = NEXT_LOCATION( extendedResponse, RESP_EXTENDED_CLOSE, 0 ); } #endif SmbStatus = SmbStatusSendResponse; Cleanup: SrvWmiEndContext(WorkContext); return SmbStatus; } // SrvSmbClose