windows-nt/Source/XPSP1/NT/base/fs/srv/smbclose.c

469 lines
13 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
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