469 lines
13 KiB
C
469 lines
13 KiB
C
|
/*++
|
|||
|
|
|||
|
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
|
|||
|
|