You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2602 lines
92 KiB
2602 lines
92 KiB
/*++
|
|
|
|
Copyright (c) 1989 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
fsctl.c
|
|
|
|
Abstract:
|
|
|
|
This module implements the mini redirector call down routines pertaining to
|
|
file system control(FSCTL) and Io Device Control (IOCTL) operations on file
|
|
system objects.
|
|
|
|
Author:
|
|
|
|
Balan Sethu Raman [SethuR] 7-March-1995
|
|
|
|
Revision History:
|
|
|
|
Joe Linn [JoeLi] -- Implemented FSCTL's
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
#include <dfsfsctl.h>
|
|
#include <ntddrdr.h>
|
|
#include <wincred.h>
|
|
#include <secpkg.h>
|
|
#include <srvfsctl.h>
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, MRxSmbFsCtl)
|
|
#pragma alloc_text(PAGE, MRxSmbNotifyChangeDirectory)
|
|
#pragma alloc_text(PAGE, MRxSmbNamedPipeFsControl)
|
|
#pragma alloc_text(PAGE, MRxSmbFsCtlUserTransact)
|
|
#pragma alloc_text(PAGE, MRxSmbMailSlotFsControl)
|
|
#pragma alloc_text(PAGE, MRxSmbFsControl)
|
|
#pragma alloc_text(PAGE, MRxSmbIoCtl)
|
|
#endif
|
|
|
|
//
|
|
// The local debug trace level
|
|
//
|
|
|
|
|
|
RXDT_DefineCategory(FSCTRL);
|
|
#define Dbg (DEBUG_TRACE_FSCTRL)
|
|
|
|
extern
|
|
NTSTATUS
|
|
MRxSmbNamedPipeFsControl(IN OUT PRX_CONTEXT RxContext);
|
|
|
|
extern
|
|
NTSTATUS
|
|
MRxSmbMailSlotFsControl(IN OUT PRX_CONTEXT RxContext);
|
|
|
|
extern
|
|
NTSTATUS
|
|
MRxSmbDfsFsControl(IN OUT PRX_CONTEXT RxContext);
|
|
|
|
extern
|
|
NTSTATUS
|
|
MRxSmbFsControl(IN OUT PRX_CONTEXT RxContext);
|
|
|
|
extern
|
|
NTSTATUS
|
|
MRxSmbFsCtlUserTransact(IN OUT PRX_CONTEXT RxContext);
|
|
|
|
NTSTATUS
|
|
MRxSmbGetPrintJobId(
|
|
IN OUT PRX_CONTEXT RxContext);
|
|
|
|
NTSTATUS
|
|
MRxSmbCoreIoCtl(
|
|
SMBPSE_ORDINARY_EXCHANGE_ARGUMENT_SIGNATURE);
|
|
|
|
NTSTATUS
|
|
MRxSmbQueryTargetInfo(
|
|
PRX_CONTEXT RxContext
|
|
);
|
|
|
|
NTSTATUS
|
|
MRxSmbQueryRemoteServerName(
|
|
PRX_CONTEXT RxContext
|
|
);
|
|
|
|
NTSTATUS
|
|
MRxSmbFsCtl(
|
|
IN OUT PRX_CONTEXT RxContext)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine performs an FSCTL operation (remote) on a file across the network
|
|
|
|
Arguments:
|
|
|
|
RxContext - the RDBSS context
|
|
|
|
Return Value:
|
|
|
|
RXSTATUS - The return status for the operation
|
|
|
|
Notes:
|
|
|
|
The FSCTL's handled by a mini rdr can be classified into one of two categories.
|
|
In the first category are those FSCTL's whose implementation are shared between
|
|
RDBSS and the mini rdr's and in the second category are those FSCTL's which
|
|
are totally implemented by the mini rdr's. To this a third category can be
|
|
added, i.e., those FSCTL's which should never be seen by the mini rdr's. The
|
|
third category is solely intended as a debugging aid.
|
|
|
|
The FSCTL's handled by a mini rdr can be classified based on functionality
|
|
|
|
--*/
|
|
{
|
|
RxCaptureFobx;
|
|
RxCaptureFcb;
|
|
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
PLOWIO_CONTEXT pLowIoContext = &RxContext->LowIoContext;
|
|
ULONG FsControlCode = pLowIoContext->ParamsFor.FsCtl.FsControlCode;
|
|
|
|
PAGED_CODE();
|
|
|
|
RxDbgTrace(+1, Dbg, ("MRxSmbFsCtl...\n", 0));
|
|
RxDbgTrace( 0, Dbg, ("MRxSmbFsCtl = %08lx\n", FsControlCode));
|
|
|
|
if (capFobx != NULL) {
|
|
PMRX_V_NET_ROOT pVNetRoot;
|
|
|
|
// Avoid device opens for which the FOBX is the VNET_ROOT instance
|
|
|
|
pVNetRoot = (PMRX_V_NET_ROOT)capFobx;
|
|
|
|
if (NodeType(pVNetRoot) != RDBSS_NTC_V_NETROOT) {
|
|
PUNICODE_STRING AlreadyPrefixedName =
|
|
GET_ALREADY_PREFIXED_NAME(capFobx->pSrvOpen,capFcb);
|
|
ULONG FcbAlreadyPrefixedNameLength = AlreadyPrefixedName->Length;
|
|
ULONG NetRootInnerNamePrefixLength = capFcb->pNetRoot->InnerNamePrefix.Length;
|
|
PWCHAR pName = AlreadyPrefixedName->Buffer;
|
|
|
|
// If an FSCTL is being attempted against the root of a share.
|
|
// The AlreadyPrefixedName associated with the FCB is the same as
|
|
// the AlreadyPrefixedName length associated with the NET_ROOT instance
|
|
// or atmost one character greater than it ( appending a \) try and
|
|
// reestablish the connection before attempting the FSCTL.
|
|
// This solves thorny issues regarding deletion/creation of shares
|
|
// on the server sides, DFS referrals etc.
|
|
|
|
if ((FcbAlreadyPrefixedNameLength == NetRootInnerNamePrefixLength) ||
|
|
((FcbAlreadyPrefixedNameLength == NetRootInnerNamePrefixLength + sizeof(WCHAR)) &&
|
|
(*((PCHAR)pName + FcbAlreadyPrefixedNameLength - sizeof(WCHAR)) ==
|
|
L'\\'))) {
|
|
if (capFobx->pSrvOpen != NULL) {
|
|
Status = SmbCeReconnect(capFobx->pSrvOpen->pVNetRoot);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (Status == STATUS_SUCCESS) {
|
|
switch (pLowIoContext->ParamsFor.FsCtl.MinorFunction) {
|
|
case IRP_MN_USER_FS_REQUEST:
|
|
case IRP_MN_TRACK_LINK :
|
|
switch (FsControlCode) {
|
|
case FSCTL_PIPE_ASSIGN_EVENT :
|
|
case FSCTL_PIPE_DISCONNECT :
|
|
case FSCTL_PIPE_LISTEN :
|
|
case FSCTL_PIPE_PEEK :
|
|
case FSCTL_PIPE_QUERY_EVENT :
|
|
case FSCTL_PIPE_TRANSCEIVE :
|
|
case FSCTL_PIPE_WAIT :
|
|
case FSCTL_PIPE_IMPERSONATE :
|
|
case FSCTL_PIPE_SET_CLIENT_PROCESS :
|
|
case FSCTL_PIPE_QUERY_CLIENT_PROCESS :
|
|
Status = MRxSmbNamedPipeFsControl(RxContext);
|
|
break;
|
|
case FSCTL_MAILSLOT_PEEK :
|
|
Status = MRxSmbMailSlotFsControl(RxContext);
|
|
break;
|
|
case FSCTL_DFS_GET_REFERRALS:
|
|
case FSCTL_DFS_REPORT_INCONSISTENCY:
|
|
Status = MRxSmbDfsFsControl(RxContext);
|
|
break;
|
|
case FSCTL_LMR_TRANSACT :
|
|
Status = MRxSmbFsCtlUserTransact(RxContext);
|
|
break;
|
|
|
|
case FSCTL_GET_PRINT_ID :
|
|
Status = MRxSmbGetPrintJobId(RxContext);
|
|
break;
|
|
|
|
case FSCTL_LMR_QUERY_TARGET_INFO:
|
|
Status = MRxSmbQueryTargetInfo(RxContext);
|
|
break;
|
|
|
|
case IOCTL_LMR_QUERY_REMOTE_SERVER_NAME:
|
|
Status = MRxSmbQueryRemoteServerName(RxContext);
|
|
break;
|
|
|
|
case FSCTL_MOVE_FILE:
|
|
case FSCTL_MARK_HANDLE:
|
|
case FSCTL_QUERY_RETRIEVAL_POINTERS:
|
|
case FSCTL_GET_VOLUME_BITMAP:
|
|
case FSCTL_GET_NTFS_FILE_RECORD:
|
|
Status = STATUS_NOT_SUPPORTED;
|
|
break;
|
|
|
|
// lwio ioctl
|
|
case FSCTL_SRV_REQUEST_RESUME_KEY:
|
|
if (NodeType(capFcb) != RDBSS_NTC_STORAGE_TYPE_FILE ||
|
|
capFcb->pNetRoot == NULL ||
|
|
capFcb->pNetRoot->pSrvCall == NULL ||
|
|
!FlagOn(capFcb->pNetRoot->pSrvCall->Flags,
|
|
SRVCALL_FLAG_LWIO_AWARE_SERVER)) {
|
|
return STATUS_NOT_SUPPORTED;
|
|
} else {
|
|
Status = MRxSmbFsControl(RxContext);
|
|
}
|
|
break;
|
|
|
|
case FSCTL_SET_REPARSE_POINT:
|
|
{
|
|
ULONG InputBufferLength = 0; // invalid value as we need an input buffer
|
|
PREPARSE_DATA_BUFFER prdBuff = (&RxContext->LowIoContext)->ParamsFor.FsCtl.pInputBuffer;
|
|
PMRX_SMB_FCB smbFcb = MRxSmbGetFcbExtension(capFcb);
|
|
|
|
InputBufferLength = (&RxContext->LowIoContext)->ParamsFor.FsCtl.InputBufferLength;
|
|
|
|
if ((prdBuff == NULL)||
|
|
(InputBufferLength < REPARSE_DATA_BUFFER_HEADER_SIZE)||
|
|
(InputBufferLength > MAXIMUM_REPARSE_DATA_BUFFER_SIZE))
|
|
{
|
|
Status = STATUS_IO_REPARSE_DATA_INVALID;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Verify that the user buffer and the data length in its header are
|
|
// internally consistent. We need to have a REPARSE_DATA_BUFFER or a
|
|
// REPARSE_GUID_DATA_BUFFER.
|
|
//
|
|
|
|
if((InputBufferLength != (ULONG)(REPARSE_DATA_BUFFER_HEADER_SIZE + prdBuff->ReparseDataLength))
|
|
&&
|
|
(InputBufferLength != (ULONG)(REPARSE_GUID_DATA_BUFFER_HEADER_SIZE + prdBuff->ReparseDataLength)))
|
|
{
|
|
Status = STATUS_IO_REPARSE_DATA_INVALID;
|
|
break;
|
|
}
|
|
}
|
|
|
|
case FSCTL_GET_REPARSE_POINT:
|
|
// absence of break intentional
|
|
case FSCTL_MARK_AS_SYSTEM_HIVE :
|
|
|
|
//
|
|
// On a remote boot machine, we need to no-op the MARK_AS_SYSTEM_HIVE
|
|
// FSCTL. Local filesystems use this to prevent a volume from being
|
|
// dismounted.
|
|
//
|
|
|
|
if (MRxSmbBootedRemotely) {
|
|
break;
|
|
}
|
|
|
|
default:
|
|
Status = MRxSmbFsControl(RxContext);
|
|
break;
|
|
}
|
|
break;
|
|
default :
|
|
Status = STATUS_INVALID_DEVICE_REQUEST;
|
|
break;
|
|
}
|
|
}
|
|
|
|
RxDbgTrace(-1, Dbg, ("MRxSmbFsCtl -> %08lx\n", Status ));
|
|
return Status;
|
|
}
|
|
|
|
typedef struct _SMB_NOTIFY_CHANGE_DIRECTORY_CONTEXT_ {
|
|
LONG ReferenceCount;
|
|
PRX_CONTEXT pRxContext;
|
|
REQ_NOTIFY_CHANGE NotifyRequest;
|
|
SMB_TRANSACTION_OPTIONS Options;
|
|
SMB_TRANSACTION_RESUMPTION_CONTEXT ResumptionContext;
|
|
} SMB_NOTIFY_CHANGE_DIRECTORY_CONTEXT, *PSMB_NOTIFY_CHANGE_DIRECTORY_CONTEXT;
|
|
|
|
|
|
NTSTATUS
|
|
FsRtlValidateChangeNotifyBuffer(
|
|
PVOID NotifyBuffer,
|
|
ULONG NotifyBufferLength )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine validates a change-notification buffer.
|
|
|
|
typedef struct _FILE_NOTIFY_INFORMATION {
|
|
ULONG NextEntryOffset;
|
|
ULONG Action;
|
|
ULONG FileNameLength;
|
|
WCHAR FileName[1];
|
|
} FILE_NOTIFY_INFORMATION, *PFILE_NOTIFY_INFORMATION;
|
|
|
|
We validate the following:
|
|
|
|
* NextEntryOffset points forward, and lies within the buffer
|
|
* FileNameLength does not bleed into the next entry
|
|
|
|
Arguments:
|
|
|
|
NotifyBuffer - The change notification buffer to be validated.
|
|
NotifyBufferLength - The size in bytes of the buffer
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS if the buffer is valid.
|
|
STATUS_INVALID_NETWORK_RESPONSE otherwise.
|
|
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
ULONG CurrentOffset = 0;
|
|
ULONG NextEntryOffset = 0;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
|
|
PFILE_NOTIFY_INFORMATION pInfo = (PFILE_NOTIFY_INFORMATION) NotifyBuffer;
|
|
|
|
//
|
|
// Return success trivially, if the buffer length is zero.
|
|
//
|
|
if( NotifyBufferLength == 0 ) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
do
|
|
{
|
|
//
|
|
// Return failure if we cannot safely read the 'NextEntryOffset'.
|
|
//
|
|
if( NotifyBufferLength < CurrentOffset + sizeof(ULONG) ) {
|
|
ASSERT( !"'NextEntryOffset' overruns buffer" );
|
|
Status = STATUS_INVALID_NETWORK_RESPONSE;
|
|
break;
|
|
}
|
|
|
|
NextEntryOffset = *((PULONG)pInfo);
|
|
if( NextEntryOffset == 0 ) {
|
|
NextEntryOffset = NotifyBufferLength - CurrentOffset;
|
|
}
|
|
|
|
//
|
|
// Make sure filename length doesnt overrun the current entry or the buffer.
|
|
//
|
|
if(( CurrentOffset + FIELD_OFFSET(FILE_NOTIFY_INFORMATION, FileName) > NotifyBufferLength ) ||
|
|
( pInfo->FileNameLength + FIELD_OFFSET(FILE_NOTIFY_INFORMATION, FileName) > NextEntryOffset ) ||
|
|
( (LONG)pInfo->FileNameLength < 0 ) ) {
|
|
|
|
ASSERT(!"ChangeNotify FileNameLength overruns buffer");
|
|
Status = STATUS_INVALID_NETWORK_RESPONSE;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// If 'NextEntryOffset' is 0, then break out
|
|
//
|
|
if( pInfo->NextEntryOffset == 0 ) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Check for backward links.
|
|
//
|
|
if( (LONG)pInfo->NextEntryOffset < 0 ) {
|
|
Status = STATUS_INVALID_NETWORK_RESPONSE;
|
|
ASSERT(!"ChangeNotify NextEntryOffset < 0");
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Check for link which overruns the buffer
|
|
//
|
|
if( CurrentOffset + pInfo->NextEntryOffset >= NotifyBufferLength ) {
|
|
Status = STATUS_INVALID_NETWORK_RESPONSE;
|
|
ASSERT(!"ChangeNotify NextEntryOffset > NotifyBufferLength");
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Check for 4 byte alignment.
|
|
//
|
|
if( pInfo->NextEntryOffset & 0x3 ) {
|
|
Status = STATUS_INVALID_NETWORK_RESPONSE;
|
|
ASSERT(!"ChangeNotify NextEntryOffset is not DWORD aligned");
|
|
break;
|
|
}
|
|
|
|
CurrentOffset += pInfo->NextEntryOffset;
|
|
pInfo = (PFILE_NOTIFY_INFORMATION) Add2Ptr( pInfo, pInfo->NextEntryOffset );
|
|
|
|
} while(1);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
MRxSmbNotifyChangeDirectorySynchronousCompletion(
|
|
PSMB_NOTIFY_CHANGE_DIRECTORY_CONTEXT pNotificationContext)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is invoked when a directory change notification operation is
|
|
completed
|
|
|
|
Arguments:
|
|
|
|
RxContext - the RDBSS context
|
|
|
|
Return Value:
|
|
|
|
RXSTATUS - The return status for the operation
|
|
|
|
Notes:
|
|
|
|
This routine will always be called. This is true even if the change directory
|
|
notification is cancelled. In such cases the memory allocated is freed without
|
|
any interaction with the wrapper. In cases of successful directory change
|
|
notification completion the appropriate completion routine is invoked and the
|
|
RxContext modified to prevent any cancellation from proceeding further.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_PENDING;
|
|
PMRXSMB_RX_CONTEXT pMRxSmbContext;
|
|
PRX_CONTEXT pRxContext;
|
|
PSMB_EXCHANGE pExchange = NULL;
|
|
BOOLEAN FinalizeNotificationContext = FALSE;
|
|
|
|
SmbCeAcquireSpinLock();
|
|
|
|
FinalizeNotificationContext =
|
|
(InterlockedDecrement(&pNotificationContext->ReferenceCount) == 0);
|
|
|
|
if (FinalizeNotificationContext) {
|
|
pRxContext = pNotificationContext->pRxContext;
|
|
|
|
pMRxSmbContext = MRxSmbGetMinirdrContext(pRxContext);
|
|
pExchange = pMRxSmbContext->pExchange;
|
|
|
|
Status = pRxContext->StoredStatus;
|
|
}
|
|
|
|
SmbCeReleaseSpinLock();
|
|
|
|
// Free the associated exchange.
|
|
if (FinalizeNotificationContext) {
|
|
if (pExchange != NULL) {
|
|
SmbCeDereferenceAndDiscardExchange(pExchange);
|
|
}
|
|
|
|
// Free the notification context.
|
|
RxFreePool(pNotificationContext);
|
|
|
|
ASSERT(Status != STATUS_PENDING);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
VOID
|
|
MRxSmbNotifyChangeDirectoryCompletion(
|
|
PSMB_NOTIFY_CHANGE_DIRECTORY_CONTEXT pNotificationContext)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is invoked when a directory change notification operation is
|
|
completed
|
|
|
|
Arguments:
|
|
|
|
RxContext - the RDBSS context
|
|
|
|
Return Value:
|
|
|
|
RXSTATUS - The return status for the operation
|
|
|
|
Notes:
|
|
|
|
This routine will always be called. This is true even if the change directory
|
|
notification is cancelled. In such cases the memory allocated is freed without
|
|
any inteaction with the wrapper. In cases of successful directory change
|
|
notification completion the appropriate completion routine is invoked and the
|
|
RxContext modified to prevent any cancellation from proceeding further.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
PMRXSMB_RX_CONTEXT pMRxSmbContext;
|
|
PRX_CONTEXT pRxContext;
|
|
PSMB_EXCHANGE pExchange = NULL;
|
|
BOOLEAN FinalizeNotificationContext = FALSE;
|
|
|
|
pRxContext = pNotificationContext->pRxContext;
|
|
|
|
if (MRxSmbNonTrivialFileName(pRxContext))
|
|
{
|
|
MRxSmbInvalidateFullDirectoryCache(pRxContext);
|
|
MRxSmbInvalidateFullDirectoryCacheParent(pRxContext,FALSE);
|
|
}
|
|
|
|
SmbCeAcquireSpinLock();
|
|
|
|
FinalizeNotificationContext =
|
|
(InterlockedDecrement(&pNotificationContext->ReferenceCount) == 0);
|
|
|
|
if (pRxContext != NULL) {
|
|
PSMB_TRANSACT_EXCHANGE pTransactExchange;
|
|
|
|
pMRxSmbContext = MRxSmbGetMinirdrContext(pRxContext);
|
|
pExchange = pMRxSmbContext->pExchange;
|
|
|
|
if (pExchange != NULL) {
|
|
PSMBCEDB_SERVER_ENTRY pServerEntry;
|
|
|
|
pTransactExchange = (PSMB_TRANSACT_EXCHANGE)pExchange;
|
|
|
|
pServerEntry = SmbCeGetExchangeServerEntry(pExchange);
|
|
|
|
if ((pNotificationContext->ResumptionContext.FinalStatusFromServer ==
|
|
STATUS_NOT_SUPPORTED) ||
|
|
(pNotificationContext->ResumptionContext.FinalStatusFromServer ==
|
|
STATUS_NOT_IMPLEMENTED)) {
|
|
pServerEntry->Server.ChangeNotifyNotSupported = TRUE;
|
|
}
|
|
|
|
//
|
|
// Validate the response buffer before returning.
|
|
//
|
|
if( pNotificationContext->ResumptionContext.FinalStatusFromServer == STATUS_SUCCESS ) {
|
|
|
|
pNotificationContext->ResumptionContext.FinalStatusFromServer =
|
|
FsRtlValidateChangeNotifyBuffer(
|
|
pRxContext->LowIoContext.ParamsFor.NotifyChangeDirectory.pNotificationBuffer,
|
|
pTransactExchange->ParamBytesReceived );
|
|
|
|
}
|
|
|
|
if( pNotificationContext->ResumptionContext.FinalStatusFromServer == STATUS_SUCCESS ) {
|
|
pRxContext->InformationToReturn = pTransactExchange->ParamBytesReceived;
|
|
} else {
|
|
pRxContext->InformationToReturn = 0;
|
|
}
|
|
|
|
} else {
|
|
pRxContext->InformationToReturn = 0;
|
|
}
|
|
|
|
pRxContext->StoredStatus =
|
|
pNotificationContext->ResumptionContext.FinalStatusFromServer;
|
|
|
|
if( (pRxContext->InformationToReturn == 0) &&
|
|
(pRxContext->StoredStatus == STATUS_SUCCESS) )
|
|
{
|
|
pRxContext->StoredStatus = STATUS_NOTIFY_ENUM_DIR;
|
|
//MRxSmbInvalidateFullDirectoryCache(pRxContext);
|
|
}
|
|
|
|
}
|
|
|
|
SmbCeReleaseSpinLock();
|
|
|
|
if (FinalizeNotificationContext) {
|
|
if (pRxContext != NULL) {
|
|
RxLowIoCompletion(pRxContext);
|
|
}
|
|
|
|
// Free the associated exchange.
|
|
if (pExchange != NULL) {
|
|
SmbCeDereferenceAndDiscardExchange(pExchange);
|
|
}
|
|
|
|
// Free the notification context.
|
|
RxFreePool(pNotificationContext);
|
|
}
|
|
}
|
|
|
|
NTSTATUS
|
|
MRxSmbNotifyChangeDirectory(
|
|
IN OUT PRX_CONTEXT RxContext)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine performs a directory change notification operation
|
|
|
|
Arguments:
|
|
|
|
RxContext - the RDBSS context
|
|
|
|
Return Value:
|
|
|
|
RXSTATUS - The return status for the operation
|
|
|
|
Notes:
|
|
|
|
A directory change notification opertaion is an asychronous operation. It
|
|
consists of sending a SMB requesting change notification whose response is
|
|
obtained when the desired change is affected on the server.
|
|
|
|
Some important points to remember are as follows .....
|
|
|
|
1) The SMB response is not obtained till the desired change is affected on
|
|
the server. Therefore an additional MID needs to be reserved on those
|
|
connections which permit multiple MID's so that a cancel SMB can be sent to
|
|
the server when a change notification is active.
|
|
|
|
2) The Change notification is typical of a long term ( response time
|
|
dictated by factors beyond the servers control). Another example is
|
|
the query FSCTL operation in CAIRO. For all these operations we initiate
|
|
an asychronous transact exchange.
|
|
|
|
3) The corresponding LowIo completion routine is invoked asynchronously.
|
|
|
|
4) This is an example of an operation for which the MINI RDR has to
|
|
register a context for handling cancellations initiated locally.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
RxCaptureFcb;
|
|
RxCaptureFobx;
|
|
PLOWIO_CONTEXT pLowIoContext = &RxContext->LowIoContext;
|
|
|
|
PSMBCEDB_SERVER_ENTRY pServerEntry;
|
|
PSMB_NOTIFY_CHANGE_DIRECTORY_CONTEXT pNotificationContext;
|
|
|
|
PBYTE pInputParamBuffer = NULL;
|
|
PBYTE pOutputParamBuffer = NULL;
|
|
PBYTE pInputDataBuffer = NULL;
|
|
PBYTE pOutputDataBuffer = NULL;
|
|
|
|
ULONG InputParamBufferLength = 0;
|
|
ULONG OutputParamBufferLength = 0;
|
|
ULONG InputDataBufferLength = 0;
|
|
ULONG OutputDataBufferLength = 0;
|
|
|
|
RxDbgTrace(+1, Dbg, ("MRxNotifyChangeDirectory...Entry\n", 0));
|
|
|
|
pServerEntry = SmbCeGetAssociatedServerEntry(capFcb->pNetRoot->pSrvCall);
|
|
|
|
// if the server entry is in disconnected state, then let CSC do change notify
|
|
// If successful, the CSC routine should return a STATUS_PENDING and
|
|
// modify the rxcontext in way that is suitable to the underlying implementation
|
|
|
|
// In the current incarnation, the CSC routine will
|
|
// a) remove the irp from the rxconetxt and b) dereference the rxcontext
|
|
|
|
if (MRxSmbIsThisADisconnectedOpen(capFobx->pSrvOpen)||
|
|
SmbCeIsServerInDisconnectedMode(pServerEntry)) {
|
|
return MRxSmbCscNotifyChangeDirectory(RxContext);
|
|
}
|
|
else if (!FlagOn(pServerEntry->Server.DialectFlags,DF_NT_SMBS) ||
|
|
pServerEntry->Server.ChangeNotifyNotSupported) {
|
|
return STATUS_NOT_SUPPORTED;
|
|
}
|
|
|
|
#if defined(REMOTE_BOOT)
|
|
//
|
|
// Reject change notify on the remote boot share. This is necessary to
|
|
// prevent overloading the server with long-term requests. (There are
|
|
// LOTS of change notifies posted on the boot device!)
|
|
//
|
|
|
|
if (MRxSmbBootedRemotely) {
|
|
PSMBCE_SESSION pSession;
|
|
pSession = &SmbCeGetAssociatedVNetRootContext(capFobx->pSrvOpen->pVNetRoot)->pSessionEntry->Session;
|
|
if (FlagOn(pSession->Flags,SMBCE_SESSION_FLAGS_REMOTE_BOOT_SESSION)) {
|
|
return STATUS_NOT_SUPPORTED;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
pNotificationContext =
|
|
(PSMB_NOTIFY_CHANGE_DIRECTORY_CONTEXT)
|
|
RxAllocatePoolWithTag(
|
|
NonPagedPool,
|
|
sizeof(SMB_NOTIFY_CHANGE_DIRECTORY_CONTEXT),
|
|
MRXSMB_FSCTL_POOLTAG);
|
|
|
|
if (pNotificationContext != NULL) {
|
|
PREQ_NOTIFY_CHANGE pNotifyRequest;
|
|
PSMB_TRANSACTION_OPTIONS pTransactionOptions;
|
|
PSMB_TRANSACTION_RESUMPTION_CONTEXT pResumptionContext;
|
|
PMRX_SMB_SRV_OPEN pSmbSrvOpen;
|
|
|
|
BOOLEAN FcbAcquired = FALSE;
|
|
|
|
RxCaptureFobx;
|
|
ASSERT (capFobx != NULL);
|
|
|
|
if (!RxIsFcbAcquiredExclusive(capFcb)) {
|
|
// ASSERT(!RxIsFcbAcquiredShared(capFcb));
|
|
Status = RxAcquireExclusiveFcbResourceInMRx( capFcb );
|
|
|
|
FcbAcquired = (Status == STATUS_SUCCESS);
|
|
}
|
|
|
|
if (FcbAcquired) {
|
|
if (FlagOn(capFobx->pSrvOpen->Flags,SRVOPEN_FLAG_CLOSED) ||
|
|
FlagOn(capFobx->pSrvOpen->Flags,SRVOPEN_FLAG_ORPHANED)) {
|
|
Status = STATUS_FILE_CLOSED;
|
|
} else {
|
|
Status = MRxSmbDeferredCreate(RxContext);
|
|
}
|
|
|
|
RxReleaseFcbResourceInMRx( capFcb );
|
|
}
|
|
|
|
if (Status==STATUS_SUCCESS) {
|
|
|
|
pSmbSrvOpen = MRxSmbGetSrvOpenExtension(capFobx->pSrvOpen);
|
|
|
|
pNotificationContext->pRxContext = RxContext;
|
|
// The reference count is set to 2. one for the async completion routine
|
|
// and one for the tail completion routine
|
|
pNotificationContext->ReferenceCount = 2;
|
|
|
|
pNotifyRequest = &(pNotificationContext->NotifyRequest);
|
|
pTransactionOptions = &(pNotificationContext->Options);
|
|
pResumptionContext = &(pNotificationContext->ResumptionContext);
|
|
|
|
pNotifyRequest->CompletionFilter = pLowIoContext->ParamsFor.NotifyChangeDirectory.CompletionFilter;
|
|
pNotifyRequest->Fid = pSmbSrvOpen->Fid;
|
|
pNotifyRequest->WatchTree = pLowIoContext->ParamsFor.NotifyChangeDirectory.WatchTree;
|
|
pNotifyRequest->Reserved = 0;
|
|
|
|
OutputParamBufferLength = pLowIoContext->ParamsFor.NotifyChangeDirectory.NotificationBufferLength;
|
|
pOutputParamBuffer = pLowIoContext->ParamsFor.NotifyChangeDirectory.pNotificationBuffer;
|
|
|
|
*pTransactionOptions = RxDefaultTransactionOptions;
|
|
pTransactionOptions->NtTransactFunction = NT_TRANSACT_NOTIFY_CHANGE;
|
|
pTransactionOptions->TimeoutIntervalInMilliSeconds = SMBCE_TRANSACTION_TIMEOUT_NOT_USED;
|
|
pTransactionOptions->Flags = SMB_XACT_FLAGS_INDEFINITE_DELAY_IN_RESPONSE;
|
|
|
|
SmbCeInitializeAsynchronousTransactionResumptionContext(
|
|
pResumptionContext,
|
|
MRxSmbNotifyChangeDirectoryCompletion,
|
|
pNotificationContext);
|
|
|
|
Status = SmbCeAsynchronousTransact(
|
|
RxContext, // the RXContext for the transaction
|
|
pTransactionOptions, // transaction options
|
|
pNotifyRequest, // the setup buffer
|
|
sizeof(REQ_NOTIFY_CHANGE), // setup buffer length
|
|
NULL,
|
|
0,
|
|
pInputParamBuffer, // Input Param Buffer
|
|
InputParamBufferLength, // Input param buffer length
|
|
pOutputParamBuffer, // Output param buffer
|
|
OutputParamBufferLength, // output param buffer length
|
|
pInputDataBuffer, // Input data buffer
|
|
InputDataBufferLength, // Input data buffer length
|
|
pOutputDataBuffer, // output data buffer
|
|
OutputDataBufferLength, // output data buffer length
|
|
pResumptionContext // the resumption context
|
|
);
|
|
|
|
ASSERT(Status == STATUS_PENDING);
|
|
|
|
Status = MRxSmbNotifyChangeDirectorySynchronousCompletion(
|
|
pNotificationContext);
|
|
|
|
} else {
|
|
NOTHING; //just return the status from the deferred open call
|
|
}
|
|
} else {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
if (MRxSmbIsThisADisconnectedOpen(capFobx->pSrvOpen)||
|
|
SmbCeIsServerInDisconnectedMode(pServerEntry)) {
|
|
return MRxSmbCscNotifyChangeDirectory(RxContext);
|
|
}
|
|
|
|
RxDbgTrace(-1, Dbg, ("MRxSmbNotifyChangeDirectory -> %08lx\n", Status ));
|
|
return Status;
|
|
}
|
|
|
|
UNICODE_STRING s_NamedPipeTransactionName = { 12,12,L"\\PIPE\\" };
|
|
UNICODE_STRING s_MailSlotTransactionName = {20,20,L"\\MAILSLOT\\" };
|
|
|
|
typedef struct _SMB_NAMED_PIPE_FSCTL_COMPLETION_CONTEXT_ {
|
|
LONG ReferenceCount;
|
|
PRX_CONTEXT pRxContext;
|
|
PWCHAR pTransactionNameBuffer;
|
|
SMB_TRANSACTION_OPTIONS Options;
|
|
SMB_TRANSACTION_RESUMPTION_CONTEXT ResumptionContext;
|
|
} SMB_NAMED_PIPE_FSCTL_COMPLETION_CONTEXT,
|
|
*PSMB_NAMED_PIPE_FSCTL_COMPLETION_CONTEXT;
|
|
|
|
VOID
|
|
MRxSmbNamedPipeFsControlCompletion(
|
|
PSMB_NAMED_PIPE_FSCTL_COMPLETION_CONTEXT pFsCtlCompletionContext)
|
|
{
|
|
PRX_CONTEXT pRxContext = NULL;
|
|
PSMB_EXCHANGE pExchange = NULL;
|
|
BOOLEAN FinalizeFsCtlCompletionContext = FALSE;
|
|
|
|
SmbCeAcquireSpinLock();
|
|
|
|
FinalizeFsCtlCompletionContext =
|
|
(InterlockedDecrement(&pFsCtlCompletionContext->ReferenceCount) == 0);
|
|
|
|
if (FinalizeFsCtlCompletionContext) {
|
|
pRxContext = pFsCtlCompletionContext->pRxContext;
|
|
|
|
if (pRxContext != NULL) {
|
|
PMRXSMB_RX_CONTEXT pMRxSmbContext;
|
|
|
|
pMRxSmbContext = MRxSmbGetMinirdrContext(pRxContext);
|
|
pExchange = pMRxSmbContext->pExchange;
|
|
}
|
|
}
|
|
|
|
SmbCeReleaseSpinLock();
|
|
|
|
if (FinalizeFsCtlCompletionContext) {
|
|
if (pRxContext != NULL) {
|
|
pRxContext->StoredStatus =
|
|
pFsCtlCompletionContext->ResumptionContext.FinalStatusFromServer;
|
|
|
|
if (pRxContext->StoredStatus == STATUS_INVALID_HANDLE) {
|
|
pRxContext->StoredStatus = STATUS_INVALID_NETWORK_RESPONSE;
|
|
}
|
|
|
|
pRxContext->InformationToReturn =
|
|
pFsCtlCompletionContext->ResumptionContext.DataBytesReceived;
|
|
|
|
RxLowIoCompletion(pRxContext);
|
|
}
|
|
|
|
if (pExchange != NULL) {
|
|
SmbCeDereferenceAndDiscardExchange(pExchange);
|
|
}
|
|
|
|
if (pFsCtlCompletionContext->pTransactionNameBuffer != NULL) {
|
|
RxFreePool(pFsCtlCompletionContext->pTransactionNameBuffer);
|
|
}
|
|
|
|
RxFreePool(pFsCtlCompletionContext);
|
|
}
|
|
}
|
|
|
|
NTSTATUS
|
|
MRxSmbNamedPipeFsControl(PRX_CONTEXT RxContext)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine handles all named pipe related FSCTL's
|
|
|
|
Arguments:
|
|
|
|
RxContext - the RDBSS context
|
|
|
|
Return Value:
|
|
|
|
RXSTATUS - The return status for the operation
|
|
|
|
--*/
|
|
{
|
|
RxCaptureFobx;
|
|
RxCaptureFcb;
|
|
|
|
PMRX_SMB_SRV_OPEN pSmbSrvOpen;
|
|
|
|
PLOWIO_CONTEXT pLowIoContext = &RxContext->LowIoContext;
|
|
ULONG FsControlCode = pLowIoContext->ParamsFor.FsCtl.FsControlCode;
|
|
UNICODE_STRING TransactionName;
|
|
|
|
BOOLEAN ReestablishConnectionIfRequired = FALSE, fTransactioNameBufferAllocated = FALSE;
|
|
|
|
NTSTATUS Status;
|
|
USHORT Setup[2];
|
|
|
|
PBYTE pInputParamBuffer = NULL;
|
|
PBYTE pOutputParamBuffer = NULL;
|
|
PBYTE pInputDataBuffer = NULL;
|
|
PBYTE pOutputDataBuffer = NULL;
|
|
|
|
ULONG InputParamBufferLength = 0;
|
|
ULONG OutputParamBufferLength = 0;
|
|
ULONG InputDataBufferLength = 0;
|
|
ULONG OutputDataBufferLength = 0;
|
|
|
|
ULONG TimeoutIntervalInMilliSeconds;
|
|
|
|
RESP_PEEK_NMPIPE PeekResponse;
|
|
KAPC_STATE ApcState;
|
|
|
|
PAGED_CODE();
|
|
|
|
RxDbgTrace(+1, Dbg, ("MRxSmbNamedPipeFsControl...\n", 0));
|
|
|
|
TimeoutIntervalInMilliSeconds = SMBCE_TRANSACTION_TIMEOUT_NOT_USED;
|
|
Status = STATUS_MORE_PROCESSING_REQUIRED;
|
|
|
|
if (NodeType(capFcb) != RDBSS_NTC_STORAGE_TYPE_FILE &&
|
|
NodeType(capFcb) != RDBSS_NTC_STORAGE_TYPE_UNKNOWN ||
|
|
capFcb->pNetRoot == NULL ||
|
|
capFcb->pNetRoot->Type != NET_ROOT_PIPE) {
|
|
return STATUS_INVALID_DEVICE_REQUEST;
|
|
}
|
|
|
|
if (capFobx != NULL) {
|
|
pSmbSrvOpen = MRxSmbGetSrvOpenExtension(capFobx->pSrvOpen);
|
|
} else {
|
|
pSmbSrvOpen = NULL;
|
|
}
|
|
|
|
//
|
|
// FSCTLs which use method_neither must be called in the context of the calling process.
|
|
//
|
|
|
|
KeStackAttachProcess (IoGetRequestorProcess(RxContext->CurrentIrp), &ApcState);
|
|
|
|
// The following switch statement serves the dual purpose of validating the parameters
|
|
// presented by the user as well as filling in the appropriate information if it is
|
|
// available locally.
|
|
// Currently there is no local caching strategy in place and consequently a network trip
|
|
// is always undertaken.
|
|
|
|
TransactionName = s_NamedPipeTransactionName;
|
|
|
|
switch (FsControlCode) {
|
|
case FSCTL_PIPE_PEEK :
|
|
{
|
|
Setup[0] = TRANS_PEEK_NMPIPE;
|
|
Setup[1] = pSmbSrvOpen->Fid;
|
|
|
|
pOutputParamBuffer = (PBYTE)&PeekResponse;
|
|
OutputParamBufferLength = sizeof(PeekResponse);
|
|
|
|
pOutputDataBuffer = (PBYTE)pLowIoContext->ParamsFor.FsCtl.pOutputBuffer +
|
|
FIELD_OFFSET(FILE_PIPE_PEEK_BUFFER,Data[0]);
|
|
OutputDataBufferLength = pLowIoContext->ParamsFor.FsCtl.OutputBufferLength -
|
|
FIELD_OFFSET(FILE_PIPE_PEEK_BUFFER,Data[0]);
|
|
|
|
if ((pLowIoContext->ParamsFor.FsCtl.pOutputBuffer != NULL) &&
|
|
(pLowIoContext->ParamsFor.FsCtl.OutputBufferLength < (ULONG)FIELD_OFFSET(FILE_PIPE_PEEK_BUFFER,Data[0]))) {
|
|
Status = STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
}
|
|
break;
|
|
case FSCTL_PIPE_TRANSCEIVE :
|
|
{
|
|
Setup[0] = TRANS_TRANSACT_NMPIPE;
|
|
Setup[1] = pSmbSrvOpen->Fid;
|
|
|
|
pInputDataBuffer = pLowIoContext->ParamsFor.FsCtl.pInputBuffer;
|
|
InputDataBufferLength = pLowIoContext->ParamsFor.FsCtl.InputBufferLength;
|
|
|
|
pOutputDataBuffer = pLowIoContext->ParamsFor.FsCtl.pOutputBuffer;
|
|
OutputDataBufferLength = pLowIoContext->ParamsFor.FsCtl.OutputBufferLength;
|
|
|
|
if (RxContext->CurrentIrp->RequestorMode != KernelMode) {
|
|
try {
|
|
if (pInputDataBuffer) {
|
|
ProbeForRead(pInputDataBuffer,InputDataBufferLength,1);
|
|
}
|
|
|
|
if (pOutputDataBuffer) {
|
|
ProbeForWrite(pOutputDataBuffer,OutputDataBufferLength,1);
|
|
}
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
Status=STATUS_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
// CODE.IMPROVEMENT -- Currently the semantics of attempting a TRANSCEIVE
|
|
// with zero bytes to be written is not very well defined. The Old Redirector
|
|
// succeeds without issuing a TRANSACT request. This needs to be resolved and
|
|
// till it is done the old semantics will be retained
|
|
|
|
//if (InputDataBufferLength == 0) {
|
|
// Status = STATUS_SUCCESS;
|
|
//}
|
|
|
|
}
|
|
break;
|
|
case FSCTL_PIPE_WAIT :
|
|
{
|
|
|
|
PFILE_PIPE_WAIT_FOR_BUFFER pWaitBuffer;
|
|
ULONG NameLength;
|
|
|
|
Setup[0] = TRANS_WAIT_NMPIPE;
|
|
Setup[1] = 0;
|
|
|
|
if ((pLowIoContext->ParamsFor.FsCtl.pInputBuffer == NULL) ||
|
|
(pLowIoContext->ParamsFor.FsCtl.InputBufferLength <
|
|
sizeof(FILE_PIPE_WAIT_FOR_BUFFER))) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
} else {
|
|
// Set up the transaction name to reflect the name of the pipe
|
|
// on which the wait operation is being performed.
|
|
pWaitBuffer = (PFILE_PIPE_WAIT_FOR_BUFFER)pLowIoContext->ParamsFor.FsCtl.pInputBuffer;
|
|
|
|
if (pWaitBuffer->NameLength + s_NamedPipeTransactionName.Length > MAXUSHORT ||
|
|
pWaitBuffer->NameLength - sizeof(WCHAR) >
|
|
pLowIoContext->ParamsFor.FsCtl.InputBufferLength - sizeof(FILE_PIPE_WAIT_FOR_BUFFER)) {
|
|
|
|
// if the name is too long to be put on a UNICIDE string,
|
|
// or the name length doesn't match the buffer length
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
if (Status == STATUS_MORE_PROCESSING_REQUIRED) {
|
|
// In the case of Wait FSCTL a reconnection attempt will be made
|
|
// if required
|
|
ReestablishConnectionIfRequired = TRUE;
|
|
|
|
TransactionName.Length = (USHORT)(s_NamedPipeTransactionName.Length +
|
|
pWaitBuffer->NameLength);
|
|
TransactionName.MaximumLength = TransactionName.Length;
|
|
TransactionName.Buffer = (PWCHAR)RxAllocatePool(PagedPool,TransactionName.Length);
|
|
if (TransactionName.Buffer != NULL) {
|
|
fTransactioNameBufferAllocated = TRUE;
|
|
RtlCopyMemory(TransactionName.Buffer,
|
|
s_NamedPipeTransactionName.Buffer,
|
|
s_NamedPipeTransactionName.Length);
|
|
|
|
RtlCopyMemory(
|
|
(PBYTE)TransactionName.Buffer + s_NamedPipeTransactionName.Length,
|
|
pWaitBuffer->Name,
|
|
pWaitBuffer->NameLength);
|
|
} else {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
if (pWaitBuffer->TimeoutSpecified) {
|
|
LARGE_INTEGER TimeWorkspace;
|
|
LARGE_INTEGER WaitForever;
|
|
|
|
WaitForever.LowPart = 0;
|
|
WaitForever.HighPart =0x80000000;
|
|
|
|
// Avoid negate of "WaitForever" since this generates an integer
|
|
// overflow exception on some machines.
|
|
|
|
if (pWaitBuffer->Timeout.QuadPart != WaitForever.QuadPart) {
|
|
TimeWorkspace.QuadPart = -pWaitBuffer->Timeout.QuadPart / 10000;
|
|
|
|
if ( TimeWorkspace.HighPart) {
|
|
// Tried to specify a larger timeout than we can select.
|
|
// set it to the Maximum we can request
|
|
TimeoutIntervalInMilliSeconds = 0xfffffffe;
|
|
} else {
|
|
TimeoutIntervalInMilliSeconds = TimeWorkspace.LowPart;
|
|
}
|
|
}
|
|
} else {
|
|
TimeoutIntervalInMilliSeconds = 0;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case FSCTL_PIPE_ASSIGN_EVENT :
|
|
case FSCTL_PIPE_QUERY_EVENT :
|
|
case FSCTL_PIPE_IMPERSONATE :
|
|
case FSCTL_PIPE_SET_CLIENT_PROCESS :
|
|
case FSCTL_PIPE_QUERY_CLIENT_PROCESS :
|
|
// These FSCTL's have not been implemented so far in NT. They will be implemented
|
|
// in a future release.
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
RxDbgTrace( 0, Dbg, ("MRxSmbNamedPipeFsControl: Unimplemented FS control code\n"));
|
|
break;
|
|
|
|
case FSCTL_PIPE_DISCONNECT :
|
|
case FSCTL_PIPE_LISTEN :
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
RxDbgTrace( 0, Dbg, ("MRxSmbNamedPipeFsControl: Invalid FS control code for redirector\n"));
|
|
break;
|
|
|
|
default:
|
|
RxDbgTrace( 0, Dbg, ("MRxSmbNamedPipeFsControl: Invalid FS control code\n"));
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
|
|
if (Status == STATUS_MORE_PROCESSING_REQUIRED) {
|
|
if (ReestablishConnectionIfRequired) {
|
|
if (capFobx != NULL) {
|
|
Status = SmbCeReconnect(capFobx->pSrvOpen->pVNetRoot);
|
|
} else {
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
} else {
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
if (Status == STATUS_SUCCESS) {
|
|
PSMB_TRANSACTION_OPTIONS pTransactionOptions;
|
|
PSMB_NAMED_PIPE_FSCTL_COMPLETION_CONTEXT pFsCtlCompletionContext;
|
|
PSMB_TRANSACTION_RESUMPTION_CONTEXT pResumptionContext;
|
|
|
|
RxDbgTrace( 0, Dbg, ("MRxSmbNamedPipeFsControl: TransactionName %ws Length %ld\n",
|
|
TransactionName.Buffer,TransactionName.Length));
|
|
|
|
pFsCtlCompletionContext =
|
|
(PSMB_NAMED_PIPE_FSCTL_COMPLETION_CONTEXT)
|
|
RxAllocatePoolWithTag(
|
|
NonPagedPool,
|
|
sizeof(SMB_NAMED_PIPE_FSCTL_COMPLETION_CONTEXT),
|
|
MRXSMB_FSCTL_POOLTAG);
|
|
|
|
if (pFsCtlCompletionContext != NULL) {
|
|
pResumptionContext = &pFsCtlCompletionContext->ResumptionContext;
|
|
pTransactionOptions = &(pFsCtlCompletionContext->Options);
|
|
|
|
if (FsControlCode != FSCTL_PIPE_PEEK) {
|
|
// The reference count is set to 2. one for the async
|
|
// completion routine and one for the tail completion routine
|
|
pFsCtlCompletionContext->pRxContext = RxContext;
|
|
pFsCtlCompletionContext->ReferenceCount = 2;
|
|
|
|
SmbCeInitializeAsynchronousTransactionResumptionContext(
|
|
pResumptionContext,
|
|
MRxSmbNamedPipeFsControlCompletion,
|
|
pFsCtlCompletionContext);
|
|
} else {
|
|
// Currently PEEK operations are synchronous
|
|
pFsCtlCompletionContext->pRxContext = NULL;
|
|
pFsCtlCompletionContext->ReferenceCount = 1;
|
|
}
|
|
|
|
*pTransactionOptions = RxDefaultTransactionOptions;
|
|
pTransactionOptions->NtTransactFunction = 0; // TRANSACT2/TRANSACT.
|
|
pTransactionOptions->pTransactionName = &TransactionName;
|
|
pTransactionOptions->Flags = SMB_XACT_FLAGS_FID_NOT_NEEDED;
|
|
pTransactionOptions->TimeoutIntervalInMilliSeconds = TimeoutIntervalInMilliSeconds;
|
|
|
|
if (TransactionName.Buffer != s_NamedPipeTransactionName.Buffer) {
|
|
pFsCtlCompletionContext->pTransactionNameBuffer =
|
|
TransactionName.Buffer;
|
|
} else {
|
|
pFsCtlCompletionContext->pTransactionNameBuffer = NULL;
|
|
}
|
|
|
|
if (FsControlCode != FSCTL_PIPE_PEEK) {
|
|
Status = SmbCeAsynchronousTransact(
|
|
RxContext, // the RXContext for the transaction
|
|
pTransactionOptions, // transaction options
|
|
Setup, // the setup buffer
|
|
sizeof(Setup), // setup buffer length
|
|
NULL,
|
|
0,
|
|
pInputParamBuffer, // Input Param Buffer
|
|
InputParamBufferLength, // Input param buffer length
|
|
pOutputParamBuffer, // Output param buffer
|
|
OutputParamBufferLength, // output param buffer length
|
|
pInputDataBuffer, // Input data buffer
|
|
InputDataBufferLength, // Input data buffer length
|
|
pOutputDataBuffer, // output data buffer
|
|
OutputDataBufferLength, // output data buffer length
|
|
pResumptionContext // the resumption context
|
|
);
|
|
|
|
if (Status != STATUS_PENDING) {
|
|
pFsCtlCompletionContext->ResumptionContext.FinalStatusFromServer
|
|
= Status;
|
|
}
|
|
|
|
MRxSmbNamedPipeFsControlCompletion(pFsCtlCompletionContext);
|
|
Status = STATUS_PENDING;
|
|
} else {
|
|
Status = SmbCeTransact(
|
|
RxContext, // the RXContext for the transaction
|
|
pTransactionOptions, // transaction options
|
|
Setup, // the setup buffer
|
|
sizeof(Setup), // setup buffer length
|
|
NULL,
|
|
0,
|
|
pInputParamBuffer, // Input Param Buffer
|
|
InputParamBufferLength, // Input param buffer length
|
|
pOutputParamBuffer, // Output param buffer
|
|
OutputParamBufferLength, // output param buffer length
|
|
pInputDataBuffer, // Input data buffer
|
|
InputDataBufferLength, // Input data buffer length
|
|
pOutputDataBuffer, // output data buffer
|
|
OutputDataBufferLength, // output data buffer length
|
|
pResumptionContext // the resumption context
|
|
);
|
|
|
|
switch (FsControlCode) {
|
|
case FSCTL_PIPE_PEEK:
|
|
{
|
|
// In the case of FSCTL_PIPE_PEEK post processing is required to package the
|
|
// results and also handle the idiosyncracies of the different servers.
|
|
// e.g.,
|
|
// Os/2 servers will allow PeekNamedPipe on closed pipes to succeed
|
|
// even if the server side of the pipe is closed.
|
|
//
|
|
// If we get the status PIPE_STATE_CLOSING from the server, then
|
|
// we need to return an error of STATUS_PIPE_DISCONNECTED, as this
|
|
// is what NPFS will do.
|
|
|
|
if (NT_SUCCESS(Status) ||
|
|
(Status == RX_MAP_STATUS(BUFFER_OVERFLOW))) {
|
|
if (pResumptionContext->ParameterBytesReceived >= sizeof(RESP_PEEK_NMPIPE)) {
|
|
if ((SmbGetAlignedUshort(&PeekResponse.NamedPipeState) & PIPE_STATE_CLOSING) &&
|
|
(PeekResponse.ReadDataAvailable == 0)) {
|
|
Status = STATUS_PIPE_DISCONNECTED;
|
|
} else {
|
|
PFILE_PIPE_PEEK_BUFFER pPeekBuffer;
|
|
|
|
pPeekBuffer = (PFILE_PIPE_PEEK_BUFFER)pLowIoContext->ParamsFor.FsCtl.pOutputBuffer;
|
|
|
|
pPeekBuffer->NamedPipeState = (ULONG)SmbGetAlignedUshort(&PeekResponse.NamedPipeState);
|
|
pPeekBuffer->ReadDataAvailable = (ULONG)PeekResponse.ReadDataAvailable;
|
|
pPeekBuffer->NumberOfMessages = MAXULONG;
|
|
pPeekBuffer->MessageLength = (ULONG)PeekResponse.MessageLength;
|
|
|
|
if (PeekResponse.MessageLength > OutputDataBufferLength) {
|
|
Status = STATUS_BUFFER_OVERFLOW;
|
|
}
|
|
}
|
|
}
|
|
|
|
RxContext->InformationToReturn =
|
|
FIELD_OFFSET(FILE_PIPE_PEEK_BUFFER, Data[0]) +
|
|
pResumptionContext->DataBytesReceived;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
RxContext->InformationToReturn =
|
|
pResumptionContext->DataBytesReceived;
|
|
break;
|
|
}
|
|
|
|
MRxSmbNamedPipeFsControlCompletion(pFsCtlCompletionContext);
|
|
}
|
|
} else {
|
|
|
|
if (fTransactioNameBufferAllocated)
|
|
{
|
|
RxFreePool(TransactionName.Buffer);
|
|
}
|
|
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
if (Status == STATUS_INVALID_HANDLE) {
|
|
Status = STATUS_INVALID_NETWORK_RESPONSE;
|
|
}
|
|
|
|
RxDbgTrace( 0, Dbg, ("MRxSmbNamedPipeFsControl(%ld): Failed .. returning %lx\n",FsControlCode,Status));
|
|
}
|
|
|
|
//
|
|
// Detach from caller process
|
|
//
|
|
KeUnstackDetachProcess(&ApcState);
|
|
|
|
RxDbgTrace(-1, Dbg, ("MRxSmbNamedPipeFsControl exit...st=%08lx\n", Status));
|
|
return Status;
|
|
}
|
|
|
|
#ifdef _WIN64
|
|
typedef struct _LMR_TRANSACTION_32 {
|
|
ULONG Type; // Type of structure
|
|
ULONG Size; // Size of fixed portion of structure
|
|
ULONG Version; // Structure version.
|
|
ULONG NameLength; // Number of bytes in name (in path
|
|
// format, e.g., \server\pipe\netapi\4)
|
|
ULONG NameOffset; // Offset of name in buffer.
|
|
BOOLEAN ResponseExpected; // Should remote system respond?
|
|
ULONG Timeout; // Timeout time in milliseconds.
|
|
ULONG SetupWords; // Number of trans setup words (may be
|
|
// 0). (setup words are input/output.)
|
|
ULONG SetupOffset; // Offset of setup (may be 0 for none).
|
|
ULONG MaxSetup; // Size of setup word array (may be 0).
|
|
ULONG ParmLength; // Input param area length (may be 0).
|
|
void * POINTER_32 ParmPtr; // Input parameter area (may be NULL).
|
|
ULONG MaxRetParmLength; // Output param. area length (may be 0).
|
|
ULONG DataLength; // Input data area length (may be 0).
|
|
void * POINTER_32 DataPtr; // Input data area (may be NULL).
|
|
ULONG MaxRetDataLength; // Output data area length (may be 0).
|
|
void * POINTER_32 RetDataPtr; // Output data area (may be NULL).
|
|
} LMR_TRANSACTION_32, *PLMR_TRANSACTION_32;
|
|
#endif
|
|
|
|
NTSTATUS
|
|
MRxSmbFsCtlUserTransact(PRX_CONTEXT RxContext)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine issues what is called a UserTransaction against the server that is serving the
|
|
connection for this file. very strange.............
|
|
|
|
Arguments:
|
|
|
|
RxContext - the RDBSS context
|
|
|
|
Return Value:
|
|
|
|
RXSTATUS - The return status for the operation
|
|
|
|
--*/
|
|
{
|
|
RxCaptureFobx;
|
|
PLOWIO_CONTEXT pLowIoContext = &RxContext->LowIoContext;
|
|
|
|
UNICODE_STRING TransactionName;
|
|
|
|
LMR_TRANSACTION InputBuffer;
|
|
ULONG InputBufferLength = pLowIoContext->ParamsFor.FsCtl.InputBufferLength;
|
|
ULONG SizeOfLmrTransaction = 0;
|
|
|
|
NTSTATUS Status;
|
|
|
|
PAGED_CODE();
|
|
|
|
if( pLowIoContext->ParamsFor.FsCtl.pInputBuffer == NULL )
|
|
{
|
|
return (Status = STATUS_INVALID_PARAMETER);
|
|
}
|
|
|
|
InputBuffer = *((PLMR_TRANSACTION)pLowIoContext->ParamsFor.FsCtl.pInputBuffer);
|
|
|
|
#ifdef _WIN64
|
|
if (IoIs32bitProcess(RxContext->CurrentIrp)) {
|
|
PLMR_TRANSACTION_32 InputBuffer32 = (PLMR_TRANSACTION_32)pLowIoContext->ParamsFor.FsCtl.pInputBuffer;
|
|
|
|
if (InputBufferLength < sizeof(LMR_TRANSACTION_32)) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
InputBuffer.Type = InputBuffer32->Type;
|
|
InputBuffer.Size = InputBuffer32->Size;
|
|
InputBuffer.Version = InputBuffer32->Version;
|
|
InputBuffer.NameLength = InputBuffer32->NameLength;
|
|
InputBuffer.NameOffset = InputBuffer32->NameOffset;
|
|
InputBuffer.ResponseExpected = InputBuffer32->ResponseExpected;
|
|
InputBuffer.Timeout = InputBuffer32->Timeout;
|
|
InputBuffer.SetupWords = InputBuffer32->SetupWords;
|
|
InputBuffer.SetupOffset = InputBuffer32->SetupOffset;
|
|
InputBuffer.MaxSetup = InputBuffer32->MaxSetup;
|
|
InputBuffer.ParmLength = InputBuffer32->ParmLength;
|
|
InputBuffer.ParmPtr = (PVOID)InputBuffer32->ParmPtr;
|
|
InputBuffer.MaxRetParmLength = InputBuffer32->MaxRetParmLength;
|
|
InputBuffer.DataLength = InputBuffer32->DataLength;
|
|
InputBuffer.DataPtr = (PVOID)InputBuffer32->DataPtr;
|
|
InputBuffer.MaxRetDataLength = InputBuffer32->MaxRetDataLength;
|
|
InputBuffer.RetDataPtr = (PVOID)InputBuffer32->RetDataPtr;
|
|
|
|
SizeOfLmrTransaction = sizeof(LMR_TRANSACTION_32);
|
|
} else {
|
|
SizeOfLmrTransaction = sizeof(LMR_TRANSACTION);
|
|
}
|
|
#else
|
|
SizeOfLmrTransaction = sizeof(LMR_TRANSACTION);
|
|
#endif
|
|
|
|
RxDbgTrace(+1, Dbg, ("MRxSmbFsCtlUserTransact...\n"));
|
|
|
|
if (InputBufferLength < SizeOfLmrTransaction) {
|
|
return(Status = STATUS_INVALID_PARAMETER);
|
|
}
|
|
|
|
if (InputBufferLength - SizeOfLmrTransaction < InputBuffer.NameLength) {
|
|
return(Status = STATUS_BUFFER_TOO_SMALL);
|
|
}
|
|
|
|
if ((InputBuffer.Type != TRANSACTION_REQUEST) ||
|
|
(InputBuffer.Version != TRANSACTION_VERSION)) {
|
|
return(Status = STATUS_INVALID_PARAMETER);
|
|
}
|
|
|
|
if (InputBuffer.NameOffset + InputBuffer.NameLength > InputBufferLength) {
|
|
return(Status = STATUS_INVALID_PARAMETER);
|
|
}
|
|
|
|
if (InputBuffer.SetupOffset + InputBuffer.SetupWords > InputBufferLength) {
|
|
return(Status = STATUS_INVALID_PARAMETER);
|
|
}
|
|
|
|
if (capFobx != NULL) {
|
|
PMRX_V_NET_ROOT pVNetRoot = (PMRX_V_NET_ROOT)capFobx;
|
|
|
|
if (NodeType(pVNetRoot) == RDBSS_NTC_V_NETROOT) {
|
|
Status = SmbCeReconnect(pVNetRoot);
|
|
} else {
|
|
Status = SmbCeReconnect(capFobx->pSrvOpen->pVNetRoot);
|
|
}
|
|
|
|
if (Status != STATUS_SUCCESS) {
|
|
return Status;
|
|
}
|
|
}
|
|
|
|
Status = STATUS_MORE_PROCESSING_REQUIRED;
|
|
|
|
TransactionName.MaximumLength = (USHORT)InputBuffer.NameLength;
|
|
TransactionName.Length = (USHORT)InputBuffer.NameLength;
|
|
TransactionName.Buffer = (PWSTR)(((PUCHAR)pLowIoContext->ParamsFor.FsCtl.pInputBuffer)+InputBuffer.NameOffset);
|
|
|
|
if (Status == STATUS_MORE_PROCESSING_REQUIRED) {
|
|
SMB_TRANSACTION_SEND_PARAMETERS SendParameters;
|
|
SMB_TRANSACTION_RECEIVE_PARAMETERS ReceiveParameters;
|
|
SMB_TRANSACTION_OPTIONS TransactionOptions = RxDefaultTransactionOptions;
|
|
SMB_TRANSACTION_RESUMPTION_CONTEXT ResumptionContext;
|
|
|
|
PUCHAR SetupBuffer = NULL;
|
|
|
|
RxDbgTrace( 0, Dbg, ("MRxSmbFsCtlUserTransact: TransactionName %ws Length %ld\n",
|
|
TransactionName.Buffer,TransactionName.Length));
|
|
|
|
TransactionOptions.NtTransactFunction = 0; // TRANSACT2/TRANSACT.
|
|
TransactionOptions.pTransactionName = &TransactionName;
|
|
TransactionOptions.Flags = SMB_XACT_FLAGS_FID_NOT_NEEDED;
|
|
|
|
if (!InputBuffer.ResponseExpected) {
|
|
TransactionOptions.Flags |= SMB_TRANSACTION_NO_RESPONSE;
|
|
}
|
|
TransactionOptions.TimeoutIntervalInMilliSeconds = InputBuffer.Timeout;
|
|
SmbCeInitializeTransactionResumptionContext(&ResumptionContext);
|
|
|
|
try {
|
|
if (InputBuffer.SetupOffset){
|
|
SetupBuffer = (PUCHAR)pLowIoContext->ParamsFor.FsCtl.pInputBuffer+InputBuffer.SetupOffset;
|
|
}
|
|
|
|
if (SetupBuffer) {
|
|
ProbeForWrite(SetupBuffer,InputBuffer.MaxSetup,1);
|
|
}
|
|
|
|
if (InputBuffer.ParmPtr) {
|
|
ProbeForWrite(InputBuffer.ParmPtr,InputBuffer.MaxRetParmLength,1);
|
|
}
|
|
|
|
if (InputBuffer.RetDataPtr) {
|
|
ProbeForWrite(InputBuffer.RetDataPtr,InputBuffer.MaxRetDataLength,1);
|
|
}
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
Status = SmbCeTransact(
|
|
RxContext,
|
|
&TransactionOptions,
|
|
SetupBuffer,
|
|
(USHORT)InputBuffer.SetupWords,
|
|
SetupBuffer,
|
|
InputBuffer.MaxSetup,
|
|
InputBuffer.ParmPtr,
|
|
InputBuffer.ParmLength,
|
|
InputBuffer.ParmPtr, // the buffer for the param information
|
|
InputBuffer.MaxRetParmLength,// the length of the param buffer
|
|
InputBuffer.DataPtr,
|
|
InputBuffer.DataLength,
|
|
InputBuffer.RetDataPtr, // the buffer for data
|
|
InputBuffer.MaxRetDataLength,// the length of the buffer
|
|
&ResumptionContext);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
//LowIoContext->ParamsFor.FsCtl.OutputBufferLength = ResumptionContext.DataBytesReceived;
|
|
#ifdef _WIN64
|
|
if (IoIs32bitProcess(RxContext->CurrentIrp)) {
|
|
PLMR_TRANSACTION_32 pInputBuffer = (PLMR_TRANSACTION_32)pLowIoContext->ParamsFor.FsCtl.pInputBuffer;
|
|
|
|
pInputBuffer->MaxRetParmLength = ResumptionContext.ParameterBytesReceived;
|
|
pInputBuffer->MaxRetDataLength = ResumptionContext.DataBytesReceived;
|
|
pInputBuffer->MaxSetup = ResumptionContext.SetupBytesReceived;
|
|
|
|
//this seems like a bad return value for iostatus.information
|
|
RxContext->InformationToReturn = SizeOfLmrTransaction + pInputBuffer->SetupWords;
|
|
} else {
|
|
PLMR_TRANSACTION pInputBuffer = (PLMR_TRANSACTION)pLowIoContext->ParamsFor.FsCtl.pInputBuffer;
|
|
|
|
pInputBuffer->MaxRetParmLength = ResumptionContext.ParameterBytesReceived;
|
|
pInputBuffer->MaxRetDataLength = ResumptionContext.DataBytesReceived;
|
|
pInputBuffer->MaxSetup = ResumptionContext.SetupBytesReceived;
|
|
|
|
//this seems like a return value for iostatus.information
|
|
RxContext->InformationToReturn = SizeOfLmrTransaction + pInputBuffer->SetupWords;
|
|
}
|
|
#else
|
|
{
|
|
PLMR_TRANSACTION pInputBuffer = (PLMR_TRANSACTION)pLowIoContext->ParamsFor.FsCtl.pInputBuffer;
|
|
|
|
pInputBuffer->MaxRetParmLength = ResumptionContext.ParameterBytesReceived;
|
|
pInputBuffer->MaxRetDataLength = ResumptionContext.DataBytesReceived;
|
|
pInputBuffer->MaxSetup = ResumptionContext.SetupBytesReceived;
|
|
|
|
//this seems like a return value for iostatus.information
|
|
RxContext->InformationToReturn = SizeOfLmrTransaction + pInputBuffer->SetupWords;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
RxDbgTrace( 0, Dbg, ("MRxSmbFsCtlUserTransact: Failed .. returning %lx\n",Status));
|
|
}
|
|
|
|
RxDbgTrace(-1, Dbg, ("MRxSmbFsCtlUserTransact exit...st=%08lx\n", Status));
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
MRxSmbMailSlotFsControl(PRX_CONTEXT RxContext)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine handles all named pipe related FSCTL's
|
|
|
|
Arguments:
|
|
|
|
RxContext - the RDBSS context
|
|
|
|
Return Value:
|
|
|
|
RXSTATUS - The return status for the operation
|
|
|
|
--*/
|
|
{
|
|
PAGED_CODE();
|
|
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NTSTATUS
|
|
MRxSmbDfsFsControl(PRX_CONTEXT RxContext)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine handles all DFS related FSCTL's
|
|
|
|
Arguments:
|
|
|
|
RxContext - the RDBSS context
|
|
|
|
Return Value:
|
|
|
|
RXSTATUS - The return status for the operation
|
|
|
|
--*/
|
|
{
|
|
RxCaptureFobx;
|
|
RxCaptureFcb;
|
|
|
|
PMRX_SMB_SRV_OPEN pSmbSrvOpen;
|
|
|
|
SMB_TRANSACTION_OPTIONS TransactionOptions = DEFAULT_TRANSACTION_OPTIONS;
|
|
SMB_TRANSACTION_RESUMPTION_CONTEXT ResumptionContext;
|
|
|
|
PLOWIO_CONTEXT pLowIoContext = &RxContext->LowIoContext;
|
|
ULONG FsControlCode = pLowIoContext->ParamsFor.FsCtl.FsControlCode;
|
|
|
|
NTSTATUS Status;
|
|
USHORT Setup;
|
|
|
|
PBYTE pInputParamBuffer = NULL;
|
|
PBYTE pOutputParamBuffer = NULL;
|
|
PBYTE pInputDataBuffer = NULL;
|
|
PBYTE pOutputDataBuffer = NULL;
|
|
|
|
ULONG InputParamBufferLength = 0;
|
|
ULONG OutputParamBufferLength = 0;
|
|
ULONG InputDataBufferLength = 0;
|
|
ULONG OutputDataBufferLength = 0;
|
|
|
|
PAGED_CODE();
|
|
|
|
RxDbgTrace(+1, Dbg, ("MRxSmbDfsFsControl...\n", 0));
|
|
|
|
if (RxContext->CurrentIrp->RequestorMode != KernelMode) {
|
|
// these FSCTLS are only su[pported from a kernel mode component
|
|
// because of parameter validation issues
|
|
return STATUS_INVALID_DEVICE_REQUEST;
|
|
}
|
|
|
|
Status = STATUS_MORE_PROCESSING_REQUIRED;
|
|
|
|
if (capFobx != NULL) {
|
|
pSmbSrvOpen = MRxSmbGetSrvOpenExtension(capFobx->pSrvOpen);
|
|
} else {
|
|
pSmbSrvOpen = NULL;
|
|
}
|
|
|
|
pInputParamBuffer = pLowIoContext->ParamsFor.FsCtl.pInputBuffer;
|
|
InputParamBufferLength = pLowIoContext->ParamsFor.FsCtl.InputBufferLength;
|
|
|
|
switch (FsControlCode) {
|
|
case FSCTL_DFS_GET_REFERRALS:
|
|
{
|
|
pOutputDataBuffer = pLowIoContext->ParamsFor.FsCtl.pOutputBuffer;
|
|
OutputDataBufferLength = pLowIoContext->ParamsFor.FsCtl.OutputBufferLength;
|
|
}
|
|
break;
|
|
case FSCTL_DFS_REPORT_INCONSISTENCY:
|
|
{
|
|
PWCHAR pDfsPathName;
|
|
//
|
|
// The input buffer from Dfs contains the path name with the inconsistency
|
|
// followed by the DFS_REFERRAL_V1 that has the inconsistency. The
|
|
// path name is sent in the Parameter section, and the DFS_REFERRAL_V1 is
|
|
// passed in the Data section. So, parse these two things out.
|
|
//
|
|
|
|
for (pDfsPathName = (PWCHAR) pInputParamBuffer;
|
|
*pDfsPathName != UNICODE_NULL && pDfsPathName < (PWCHAR)pInputParamBuffer+InputParamBufferLength/sizeof(WCHAR);
|
|
pDfsPathName++) {
|
|
NOTHING;
|
|
}
|
|
|
|
pDfsPathName++; // Get past the NULL char
|
|
|
|
InputParamBufferLength = (ULONG) (((PCHAR)pDfsPathName) - ((PCHAR)pInputParamBuffer));
|
|
|
|
if (InputParamBufferLength >= pLowIoContext->ParamsFor.FsCtl.InputBufferLength) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
} else {
|
|
pInputDataBuffer = (PBYTE)pDfsPathName;
|
|
InputDataBufferLength = pLowIoContext->ParamsFor.FsCtl.InputBufferLength -
|
|
InputParamBufferLength;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
ASSERT(!"Valid Dfs FSCTL");
|
|
}
|
|
|
|
if (Status == STATUS_MORE_PROCESSING_REQUIRED) {
|
|
Setup = TRANS2_GET_DFS_REFERRAL;
|
|
|
|
TransactionOptions.NtTransactFunction = 0; // TRANSACT2/TRANSACT.
|
|
TransactionOptions.pTransactionName = NULL;
|
|
TransactionOptions.TimeoutIntervalInMilliSeconds = SMBCE_TRANSACTION_TIMEOUT_NOT_USED;
|
|
|
|
Status = SmbCeTransact(
|
|
RxContext, // the RXContext for the transaction
|
|
&TransactionOptions, // transaction options
|
|
&Setup, // the setup buffer
|
|
sizeof(Setup), // setup buffer length
|
|
NULL,
|
|
0,
|
|
pInputParamBuffer, // Input Param Buffer
|
|
InputParamBufferLength, // Input param buffer length
|
|
pOutputParamBuffer, // Output param buffer
|
|
OutputParamBufferLength, // output param buffer length
|
|
pInputDataBuffer, // Input data buffer
|
|
InputDataBufferLength, // Input data buffer length
|
|
pOutputDataBuffer, // output data buffer
|
|
OutputDataBufferLength, // output data buffer length
|
|
&ResumptionContext // the resumption context
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
RxDbgTrace( 0, Dbg, ("MRxSmbDfsFsControl(%ld): Failed .. returning %lx\n",FsControlCode,Status));
|
|
} else {
|
|
RxContext->InformationToReturn = ResumptionContext.DataBytesReceived;
|
|
}
|
|
}
|
|
|
|
RxDbgTrace(-1, Dbg, ("MRxSmbDfsFsControl exit...st=%08lx\n", Status));
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
MRxSmbFsControl(PRX_CONTEXT RxContext)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine handles all the FSCTL's
|
|
|
|
Arguments:
|
|
|
|
RxContext - the RDBSS context
|
|
|
|
Return Value:
|
|
|
|
RXSTATUS - The return status for the operation
|
|
|
|
Notes:
|
|
|
|
Remoting of FSCTL's is permitted only to NT servers.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
RxCaptureFcb;
|
|
RxCaptureFobx;
|
|
|
|
PMRX_SMB_SRV_OPEN pSmbSrvOpen;
|
|
|
|
PSMBCEDB_SERVER_ENTRY pServerEntry;
|
|
|
|
PSMBCE_V_NET_ROOT_CONTEXT pVNetRootContext;
|
|
PMRX_SRV_OPEN SrvOpen = RxContext->pRelevantSrvOpen;
|
|
|
|
PFILE_OBJECT pTargetFileObject = NULL;
|
|
|
|
PLOWIO_CONTEXT pLowIoContext = &RxContext->LowIoContext;
|
|
ULONG FsControlCode = pLowIoContext->ParamsFor.FsCtl.FsControlCode;
|
|
|
|
REQ_NT_IO_CONTROL FsCtlSetup;
|
|
|
|
PBYTE pInputParamBuffer = NULL;
|
|
PBYTE pOutputParamBuffer = NULL;
|
|
PBYTE pInputDataBuffer = NULL;
|
|
PBYTE pOutputDataBuffer = NULL;
|
|
#ifdef _WIN64
|
|
PBYTE pThunkedInputData = NULL;
|
|
ULONG ThunkedInputDataLength = 0;
|
|
#endif
|
|
|
|
ULONG InputParamBufferLength = 0;
|
|
ULONG OutputParamBufferLength = 0;
|
|
ULONG InputDataBufferLength = 0;
|
|
ULONG OutputDataBufferLength = 0;
|
|
|
|
USHORT FileOrTreeId;
|
|
|
|
SMB_TRANSACTION_OPTIONS TransactionOptions = DEFAULT_TRANSACTION_OPTIONS;
|
|
SMB_TRANSACTION_SEND_PARAMETERS SendParameters;
|
|
SMB_TRANSACTION_RECEIVE_PARAMETERS ReceiveParameters;
|
|
SMB_TRANSACTION_RESUMPTION_CONTEXT ResumptionContext;
|
|
BOOL CscAgentConnection = FALSE;
|
|
|
|
PAGED_CODE();
|
|
|
|
if (NodeType(capFcb) == RDBSS_NTC_DEVICE_FCB) {
|
|
return STATUS_INVALID_DEVICE_REQUEST;
|
|
}
|
|
|
|
if ((FsControlCode == FSCTL_LMR_SET_LINK_TRACKING_INFORMATION) &&
|
|
(RxContext->MinorFunction != IRP_MN_TRACK_LINK)) {
|
|
return STATUS_INVALID_DEVICE_REQUEST;
|
|
}
|
|
|
|
pServerEntry = SmbCeGetAssociatedServerEntry(capFcb->pNetRoot->pSrvCall);
|
|
pVNetRootContext = SmbCeGetAssociatedVNetRootContext(SrvOpen->pVNetRoot);
|
|
|
|
if (pVNetRootContext != NULL &&
|
|
FlagOn( // agent call
|
|
pVNetRootContext->Flags,
|
|
SMBCE_V_NET_ROOT_CONTEXT_CSCAGENT_INSTANCE))
|
|
{
|
|
CscAgentConnection = TRUE;
|
|
|
|
}
|
|
|
|
|
|
if (capFobx != NULL &&
|
|
!CscAgentConnection &&
|
|
(MRxSmbIsThisADisconnectedOpen(capFobx->pSrvOpen) ||
|
|
SmbCeIsServerInDisconnectedMode(pServerEntry)) ) {
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
RxDbgTrace(+1, Dbg, ("MRxSmbFsControl...Entry FsControlCode(%lx)\n", FsControlCode));
|
|
|
|
FsCtlSetup.IsFlags = 0;
|
|
|
|
if (capFobx != NULL) {
|
|
if (NodeType(capFobx) == RDBSS_NTC_V_NETROOT) {
|
|
PMRX_V_NET_ROOT pVNetRoot = (PMRX_V_NET_ROOT)capFobx;
|
|
|
|
PSMBCE_V_NET_ROOT_CONTEXT pVNetRootContext;
|
|
|
|
pVNetRootContext = SmbCeGetAssociatedVNetRootContext(pVNetRoot);
|
|
|
|
// It is a root open the tree id needs to be sent to the server.
|
|
FileOrTreeId = pVNetRootContext->TreeId;
|
|
} else {
|
|
if (FsControlCode != FSCTL_LMR_GET_CONNECTION_INFO) {
|
|
pSmbSrvOpen = MRxSmbGetSrvOpenExtension(capFobx->pSrvOpen);
|
|
|
|
if (FlagOn(pSmbSrvOpen->Flags,SMB_SRVOPEN_FLAG_NOT_REALLY_OPEN)) {
|
|
BOOLEAN FcbAcquired = FALSE;
|
|
|
|
if (!RxIsFcbAcquiredExclusive(capFcb)) {
|
|
// This assert does not take into account the fact that other threads may
|
|
// own the resource shared, in which case we DO want to block and wait for
|
|
// the resource.
|
|
//ASSERT(!RxIsFcbAcquiredShared(capFcb));
|
|
Status = RxAcquireExclusiveFcbResourceInMRx( capFcb );
|
|
|
|
FcbAcquired = (Status == STATUS_SUCCESS);
|
|
} else {
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
if (Status == STATUS_SUCCESS) {
|
|
Status = MRxSmbDeferredCreate(RxContext);
|
|
|
|
if (FcbAcquired)
|
|
RxReleaseFcbResourceInMRx( capFcb );
|
|
}
|
|
|
|
if (Status!=STATUS_SUCCESS) {
|
|
goto FINALLY;
|
|
}
|
|
}
|
|
|
|
FileOrTreeId = pSmbSrvOpen->Fid;
|
|
} else {
|
|
FileOrTreeId = 0;
|
|
}
|
|
}
|
|
} else {
|
|
FileOrTreeId = 0;
|
|
}
|
|
|
|
SmbPutAlignedUshort(&FsCtlSetup.Fid,FileOrTreeId);
|
|
|
|
SmbPutAlignedUlong(&FsCtlSetup.FunctionCode,FsControlCode);
|
|
FsCtlSetup.IsFsctl = TRUE;
|
|
|
|
TransactionOptions.NtTransactFunction = NT_TRANSACT_IOCTL;
|
|
TransactionOptions.pTransactionName = NULL;
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
pInputDataBuffer = pLowIoContext->ParamsFor.FsCtl.pInputBuffer;
|
|
InputDataBufferLength = pLowIoContext->ParamsFor.FsCtl.InputBufferLength;
|
|
|
|
pOutputDataBuffer = pLowIoContext->ParamsFor.FsCtl.pOutputBuffer;
|
|
OutputDataBufferLength = pLowIoContext->ParamsFor.FsCtl.OutputBufferLength;
|
|
|
|
switch (FsControlCode & 3) {
|
|
case METHOD_NEITHER:
|
|
{
|
|
ULONG Device;
|
|
|
|
Device = FsControlCode >> 16;
|
|
|
|
if (Device != FILE_DEVICE_FILE_SYSTEM) {
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
if (RxContext->CurrentIrp->RequestorMode != KernelMode) {
|
|
try {
|
|
if (pInputDataBuffer) {
|
|
ProbeForRead(pInputDataBuffer,InputDataBufferLength,1);
|
|
}
|
|
|
|
if (pOutputDataBuffer) {
|
|
ProbeForWrite(pOutputDataBuffer,OutputDataBufferLength,1);
|
|
}
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
}
|
|
// fall thru.. for those FSContolcodes that belong to FILE_DEVICE_FILE_SYSTEM
|
|
// for which METHOD_NEITHER is specified we treat them as METHOD_BUFFERED
|
|
// Not Yet implemented
|
|
|
|
case METHOD_BUFFERED:
|
|
case METHOD_IN_DIRECT:
|
|
case METHOD_OUT_DIRECT:
|
|
break;
|
|
|
|
default:
|
|
ASSERT(!"Valid Method for Fs Control");
|
|
return STATUS_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
|
|
#ifdef _WIN64
|
|
pThunkedInputData = pInputDataBuffer;
|
|
ThunkedInputDataLength = InputDataBufferLength;
|
|
#endif
|
|
|
|
// There is one FSCTL for which certain amount of preprocessing is required
|
|
// This is because the I/O subsystem passes in the information as a file
|
|
// object. The FID for the file object needs to be determined and the
|
|
// appropriate FID passed to the server instead of the file object.
|
|
|
|
if (FsControlCode == FSCTL_LMR_SET_LINK_TRACKING_INFORMATION) {
|
|
PREMOTE_LINK_TRACKING_INFORMATION pRemoteLinkInformation;
|
|
|
|
PMRX_FOBX pMRxFobx;
|
|
PMRX_SRV_OPEN pSrvOpen;
|
|
PMRX_SMB_SRV_OPEN pSmbSrvOpen;
|
|
|
|
try {
|
|
pRemoteLinkInformation =
|
|
(PREMOTE_LINK_TRACKING_INFORMATION)pInputDataBuffer;
|
|
|
|
if (pRemoteLinkInformation != NULL) {
|
|
pTargetFileObject = (PFILE_OBJECT)pRemoteLinkInformation->TargetFileObject;
|
|
|
|
if (pTargetFileObject != NULL) {
|
|
// Deduce the FID and substitute it for the File Object before shipping
|
|
// the FSCTL to the server.
|
|
|
|
pMRxFobx = (PMRX_FOBX)pTargetFileObject->FsContext2;
|
|
pSrvOpen = pMRxFobx->pSrvOpen;
|
|
|
|
pSmbSrvOpen = MRxSmbGetSrvOpenExtension(pSrvOpen);
|
|
|
|
if (pSmbSrvOpen != NULL) {
|
|
pRemoteLinkInformation->TargetFileObject =
|
|
(PVOID)(pSmbSrvOpen->Fid);
|
|
} else {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
#ifdef _WIN64
|
|
if( NT_SUCCESS(Status) )
|
|
{
|
|
ThunkedInputDataLength = InputDataBufferLength;
|
|
pThunkedInputData = Smb64ThunkRemoteLinkTrackingInfo( pInputDataBuffer, &ThunkedInputDataLength, &Status );
|
|
}
|
|
#endif
|
|
} else {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
}
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
Status=STATUS_INVALID_PARAMETER;
|
|
}
|
|
} else if (FsControlCode == FSCTL_LMR_GET_CONNECTION_INFO) {
|
|
PSMBCEDB_SERVER_ENTRY pServerEntry= SmbCeGetAssociatedServerEntry(capFcb->pNetRoot->pSrvCall);
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
|
|
if (pOutputDataBuffer && (OutputDataBufferLength == sizeof(LMR_CONNECTION_INFO_3))) {
|
|
if (!memcmp(pOutputDataBuffer,EA_NAME_CSCAGENT,sizeof(EA_NAME_CSCAGENT))) {
|
|
MRxSmbGetConnectInfoLevel3Fields(
|
|
(PLMR_CONNECTION_INFO_3)(pOutputDataBuffer),
|
|
pServerEntry,
|
|
TRUE);
|
|
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
goto FINALLY;
|
|
}
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
#ifdef _WIN64
|
|
ASSERT( !( (FsControlCode == FSCTL_LMR_SET_LINK_TRACKING_INFORMATION) &&
|
|
(pThunkedInputData == NULL) ) );
|
|
#endif
|
|
|
|
Status = SmbCeTransact(
|
|
RxContext,
|
|
&TransactionOptions,
|
|
&FsCtlSetup,
|
|
sizeof(FsCtlSetup),
|
|
&FsCtlSetup,
|
|
sizeof(FsCtlSetup),
|
|
pInputParamBuffer,
|
|
InputParamBufferLength,
|
|
pOutputParamBuffer,
|
|
OutputParamBufferLength,
|
|
#ifndef _WIN64
|
|
pInputDataBuffer,
|
|
InputDataBufferLength,
|
|
#else
|
|
pThunkedInputData,
|
|
ThunkedInputDataLength,
|
|
#endif
|
|
pOutputDataBuffer,
|
|
OutputDataBufferLength,
|
|
&ResumptionContext);
|
|
|
|
RxContext->InformationToReturn = ResumptionContext.DataBytesReceived;
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
PMRX_SRV_OPEN SrvOpen = RxContext->pRelevantSrvOpen;
|
|
PMRX_SMB_SRV_OPEN smbSrvOpen = MRxSmbGetSrvOpenExtension(SrvOpen);
|
|
|
|
// invalidate the name based file info cache since it could change the attribute
|
|
// of the file on the server, i.e. FILE_ATTRIBUTE_COMPRESSED.
|
|
MRxSmbInvalidateFileInfoCache(RxContext);
|
|
|
|
// Mark FullDir Cache, weak for bdi : Current Invalidate for correctness
|
|
MRxSmbInvalidateFullDirectoryCacheParent(RxContext, TRUE);
|
|
|
|
// update the Fcb in case of reuse since the time stamp may have changed
|
|
ClearFlag(capFcb->FcbState,FCB_STATE_TIME_AND_SIZE_ALREADY_SET);
|
|
|
|
if( RxContext->InformationToReturn > OutputDataBufferLength ) {
|
|
Status = STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
}
|
|
|
|
if( !NT_SUCCESS( Status ) ) {
|
|
// RxContext->InformationToReturn = 0;
|
|
RxDbgTrace(0,Dbg,("MRxSmbFsControl: Transaction Request Completion Status %lx\n",Status));
|
|
}
|
|
}
|
|
|
|
FINALLY:
|
|
|
|
if( FsControlCode == FSCTL_LMR_SET_LINK_TRACKING_INFORMATION )
|
|
{
|
|
#ifdef _WIN64
|
|
Smb64ReleaseThunkData( pThunkedInputData );
|
|
#endif
|
|
|
|
if( pTargetFileObject != NULL )
|
|
{
|
|
PREMOTE_LINK_TRACKING_INFORMATION pRemoteLinkInformation;
|
|
|
|
pRemoteLinkInformation =
|
|
(PREMOTE_LINK_TRACKING_INFORMATION)pInputDataBuffer;
|
|
|
|
pRemoteLinkInformation->TargetFileObject = pTargetFileObject;
|
|
}
|
|
}
|
|
|
|
RxDbgTrace(-1, Dbg, ("MRxSmbFsControl...Exit\n"));
|
|
return Status;
|
|
}
|
|
|
|
#if DBG
|
|
NTSTATUS
|
|
MRxSmbTestForLowIoIoctl(
|
|
IN PRX_CONTEXT RxContext
|
|
)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
RxCaptureFcb;RxCaptureFobx;
|
|
PLOWIO_CONTEXT LowIoContext = &RxContext->LowIoContext;
|
|
PSZ Buffer = (PSZ)(LowIoContext->ParamsFor.IoCtl.pInputBuffer);
|
|
ULONG OutputBufferLength = LowIoContext->ParamsFor.IoCtl.OutputBufferLength;
|
|
ULONG InputBufferLength = LowIoContext->ParamsFor.IoCtl.InputBufferLength;
|
|
UNICODE_STRING u;
|
|
PUNICODE_STRING FileName = GET_ALREADY_PREFIXED_NAME(capFobx->pSrvOpen,capFcb);
|
|
ULONG ReturnLength;
|
|
|
|
PAGED_CODE();
|
|
|
|
ReturnLength = OutputBufferLength;
|
|
if (ReturnLength > FileName->Length) {
|
|
ReturnLength = FileName->Length;
|
|
}
|
|
|
|
RxDbgTrace(0, Dbg,
|
|
("Here in MRxSmbTestForLowIoIoctl %s, obl = %08lx, rl=%08lx\n", Buffer, OutputBufferLength, ReturnLength));
|
|
|
|
// return an obvious string to make sure that darryl is copying the results out correctly
|
|
// need to check the lengths i.e. need outputl<=inputl; also need to check that count and buffer
|
|
// are aligned for wchar
|
|
|
|
RtlCopyMemory(Buffer,FileName->Buffer,ReturnLength);
|
|
u.Buffer = (PWCHAR)(Buffer);
|
|
u.Length = u.MaximumLength = (USHORT)ReturnLength;
|
|
RtlUpcaseUnicodeString(&u,&u,FALSE);
|
|
|
|
RxContext->InformationToReturn =
|
|
//LowIoContext->ParamsFor.IoCtl.OutputBufferLength =
|
|
ReturnLength;
|
|
|
|
return(Status);
|
|
}
|
|
#endif //if DBG
|
|
|
|
NTSTATUS
|
|
MRxSmbIoCtl(
|
|
IN OUT PRX_CONTEXT RxContext)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine performs an IOCTL operation. Currently, no calls are remoted; in
|
|
fact, the only call accepted is for debugging.
|
|
|
|
Arguments:
|
|
|
|
RxContext - the RDBSS context
|
|
|
|
Return Value:
|
|
|
|
RXSTATUS - The return status for the operation
|
|
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_INVALID_DEVICE_REQUEST;
|
|
|
|
PLOWIO_CONTEXT pLowIoContext = &RxContext->LowIoContext;
|
|
ULONG IoControlCode = pLowIoContext->ParamsFor.IoCtl.IoControlCode;
|
|
|
|
PAGED_CODE();
|
|
|
|
RxDbgTrace(+1, Dbg, ("MRxSmbIoCtl...\n", 0));
|
|
RxDbgTrace( 0, Dbg, ("MRxSmbIoCtl = %08lx\n", IoControlCode));
|
|
|
|
switch (IoControlCode) {
|
|
#if DBG
|
|
case IOCTL_LMMR_TESTLOWIO:
|
|
Status = MRxSmbTestForLowIoIoctl(RxContext);
|
|
break;
|
|
#endif //if DBG
|
|
default:
|
|
break;
|
|
}
|
|
|
|
RxDbgTrace(-1, Dbg, ("MRxSmbIoCtl -> %08lx\n", Status ));
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
MRxSmbGetPrintJobId(
|
|
IN OUT PRX_CONTEXT RxContext)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine performs an FSCTL operation (remote) on a file across the network
|
|
|
|
Arguments:
|
|
|
|
RxContext - the RDBSS context
|
|
|
|
Return Value:
|
|
|
|
RXSTATUS - The return status for the operation
|
|
|
|
Notes:
|
|
|
|
The FSCTL's handled by a mini rdr can be classified into one of two categories.
|
|
In the first category are those FSCTL's whose implementation are shared between
|
|
RDBSS and the mini rdr's and in the second category are those FSCTL's which
|
|
are totally implemented by the mini rdr's. To this a third category can be
|
|
added, i.e., those FSCTL's which should never be seen by the mini rdr's. The
|
|
third category is solely intended as a debugging aid.
|
|
|
|
The FSCTL's handled by a mini rdr can be classified based on functionality
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
BOOLEAN FinalizationComplete;
|
|
|
|
PSMB_PSE_ORDINARY_EXCHANGE OrdinaryExchange = NULL;
|
|
|
|
PSMBSTUFFER_BUFFER_STATE StufferState;
|
|
|
|
RxCaptureFobx;
|
|
RxCaptureFcb;
|
|
PIO_STACK_LOCATION IrpSp = RxContext->CurrentIrpSp;
|
|
|
|
PSMBCEDB_SERVER_ENTRY pServerEntry;
|
|
|
|
PAGED_CODE();
|
|
|
|
RxDbgTrace(+1, Dbg, ("MRxSmbGetPrintJobId\n", 0 ));
|
|
|
|
if (capFcb == NULL || capFobx == NULL) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (NodeType(capFcb) == RDBSS_NTC_DEVICE_FCB) {
|
|
return STATUS_INVALID_DEVICE_REQUEST;
|
|
}
|
|
|
|
pServerEntry = SmbCeGetAssociatedServerEntry(capFcb->pNetRoot->pSrvCall);
|
|
|
|
if (FlagOn(pServerEntry->Server.DialectFlags,DF_NT_SMBS)) {
|
|
Status = MRxSmbFsControl(RxContext);
|
|
goto FINALLY;
|
|
}
|
|
|
|
if (IrpSp->Parameters.FileSystemControl.OutputBufferLength < sizeof(QUERY_PRINT_JOB_INFO) ) {
|
|
Status = STATUS_BUFFER_TOO_SMALL;
|
|
goto FINALLY;
|
|
}
|
|
|
|
Status= SmbPseCreateOrdinaryExchange(
|
|
RxContext,
|
|
capFobx->pSrvOpen->pVNetRoot,
|
|
SMBPSE_OE_FROM_GETPRINTJOBID,
|
|
MRxSmbCoreIoCtl,
|
|
&OrdinaryExchange
|
|
);
|
|
|
|
if (Status != STATUS_SUCCESS) {
|
|
RxDbgTrace(-1, Dbg, ("Couldn't get the smb buf!\n"));
|
|
goto FINALLY;
|
|
}
|
|
|
|
OrdinaryExchange->AssociatedStufferState.CurrentCommand = SMB_COM_NO_ANDX_COMMAND;
|
|
|
|
StufferState = &OrdinaryExchange->AssociatedStufferState;
|
|
MRxSmbSetInitialSMB(StufferState STUFFERTRACE(Dbg,'FC'));
|
|
|
|
Status = SmbPseInitiateOrdinaryExchange(OrdinaryExchange);
|
|
|
|
FinalizationComplete = SmbPseFinalizeOrdinaryExchange(OrdinaryExchange);
|
|
ASSERT(FinalizationComplete);
|
|
|
|
FINALLY:
|
|
RxDbgTrace(-1, Dbg, ("MRxSmbIsValidDirectory exit with status=%08lx\n", Status ));
|
|
return(Status);
|
|
}
|
|
|
|
#define SPOOLER_DEVICE 0x53
|
|
#define GET_PRINTER_ID 0x60
|
|
|
|
typedef struct _SMB_RESP_PRINT_JOB_ID {
|
|
USHORT JobId;
|
|
UCHAR ServerName[LM20_CNLEN+1];
|
|
UCHAR QueueName[LM20_QNLEN+1];
|
|
UCHAR Padding; // Unknown what this padding is..
|
|
} SMB_RESP_PRINT_JOB_ID, *PSMB_RESP_PRINT_JOB_ID;
|
|
|
|
NTSTATUS
|
|
MRxSmbCoreIoCtl(
|
|
SMBPSE_ORDINARY_EXCHANGE_ARGUMENT_SIGNATURE
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the start routine for SMB IOCTL. This initiates the construction of the
|
|
appropriate SMB.
|
|
|
|
Arguments:
|
|
|
|
pExchange - the exchange instance
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - The return status for the operation
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = RX_MAP_STATUS(NOT_IMPLEMENTED);
|
|
PSMBSTUFFER_BUFFER_STATE StufferState = &OrdinaryExchange->AssociatedStufferState;
|
|
|
|
RxCaptureFcb; RxCaptureFobx;
|
|
PMRX_SRV_OPEN SrvOpen = RxContext->pRelevantSrvOpen;
|
|
PMRX_SMB_SRV_OPEN smbSrvOpen = MRxSmbGetSrvOpenExtension(SrvOpen);
|
|
|
|
PMRX_SMB_FOBX smbFobx = MRxSmbGetFileObjectExtension(capFobx);
|
|
|
|
PSMBCE_SERVER pServer = SmbCeGetExchangeServer(OrdinaryExchange);
|
|
ULONG SmbLength;
|
|
|
|
PAGED_CODE();
|
|
|
|
RxDbgTrace(+1, Dbg, ("MRxSmbCoreIoCtl\n", 0 ));
|
|
|
|
switch (OrdinaryExchange->EntryPoint) {
|
|
case SMBPSE_OE_FROM_GETPRINTJOBID:
|
|
COVERED_CALL(
|
|
MRxSmbStartSMBCommand(
|
|
StufferState,
|
|
SetInitialSMB_ForReuse,
|
|
SMB_COM_IOCTL,
|
|
SMB_REQUEST_SIZE(IOCTL),
|
|
NO_EXTRA_DATA,
|
|
SMB_BEST_ALIGNMENT(1,0),
|
|
RESPONSE_HEADER_SIZE_NOT_SPECIFIED,
|
|
0,0,0,0 STUFFERTRACE(Dbg,'FC'))
|
|
);
|
|
|
|
MRxSmbDumpStufferState (1100,"SMB w/ GFA before stuffing",StufferState);
|
|
|
|
//CODE.IMPROVEMENT if this is truly core, we have to copy the name since its in UNICODE
|
|
// otherwise, we don't need to copy the name here, we can just Mdl like in writes
|
|
MRxSmbStuffSMB (StufferState,
|
|
"0wwwwwwwwwwwwwwB!",
|
|
// 0 UCHAR WordCount; // Count of parameter words = 8
|
|
smbSrvOpen->Fid, // w _USHORT( Fid ); // File handle
|
|
SPOOLER_DEVICE, // w _USHORT( Category);
|
|
GET_PRINTER_ID, // w _USHORT( Function ); // Device function
|
|
0, // w _USHORT( TotalParameterCount ); // Total parameter bytes being sent
|
|
0, // w _USHORT( TotalDataCount ); // Total data bytes being sent
|
|
0, // w _USHORT( MaxParameterCount ); // Max parameter bytes to return
|
|
0, // w _USHORT( MaxDataCount ); // Max data bytes to return
|
|
0, // w _ULONG ( Timeout );
|
|
0, // w _USHORT( Reserved );
|
|
0, // w _USHORT( ParameterCount ); // Parameter bytes sent this buffer
|
|
0, // w _USHORT( ParameterOffset ); // Offset (from header start) to params
|
|
0, // w _USHORT( DataCount ); // Data bytes sent this buffer
|
|
0, // w _USHORT( DataOffset ); // Offset (from header start) to data
|
|
0, // w _USHORT( ByteCount ); // Count of data bytes
|
|
SMB_WCT_CHECK(14) 0 // _USHORT( ByteCount ); // Count of data bytes; min = 0
|
|
// UCHAR Buffer[1]; // Reserved buffer
|
|
);
|
|
|
|
break;
|
|
|
|
default:
|
|
Status = STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
if (Status == STATUS_SUCCESS) {
|
|
MRxSmbDumpStufferState (700,"SMB w/ GFA after stuffing",StufferState);
|
|
|
|
Status = SmbPseOrdinaryExchange(
|
|
SMBPSE_ORDINARY_EXCHANGE_ARGUMENTS,
|
|
SMBPSE_OETYPE_IOCTL
|
|
);
|
|
}
|
|
|
|
|
|
FINALLY:
|
|
RxDbgTrace(-1, Dbg, ("MRxSmbSynchronousGetFileAttributes exiting.......OE=%08lx, st=%08lx\n",OrdinaryExchange,Status));
|
|
return(Status);
|
|
}
|
|
|
|
NTSTATUS
|
|
MRxSmbFinishCoreIoCtl(
|
|
PSMB_PSE_ORDINARY_EXCHANGE OrdinaryExchange,
|
|
PRESP_IOCTL Response
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine copies the print job ID and server and queue name to the user buffer.
|
|
|
|
Arguments:
|
|
|
|
OrdinaryExchange - the exchange instance
|
|
Response - the response
|
|
|
|
Return Value:
|
|
|
|
RXSTATUS - The return status for the operation
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = RX_MAP_STATUS(SUCCESS);
|
|
PRX_CONTEXT RxContext = OrdinaryExchange->RxContext;
|
|
|
|
PIRP Irp = RxContext->CurrentIrp;
|
|
PIO_STACK_LOCATION IrpSp = RxContext->CurrentIrpSp;
|
|
|
|
PAGED_CODE();
|
|
|
|
RxDbgTrace(+1, Dbg, ("MRxSmbFinishCoreIoCtl\n", 0 ));
|
|
SmbPseOEAssertConsistentLinkageFromOE("MRxSmbFinishCoreIoCtl:");
|
|
|
|
switch (OrdinaryExchange->EntryPoint) {
|
|
case SMBPSE_OE_FROM_GETPRINTJOBID:
|
|
if (Response->WordCount != 8 ||
|
|
SmbGetUshort(&Response->DataCount) != sizeof(SMB_RESP_PRINT_JOB_ID)) {
|
|
Status = STATUS_INVALID_NETWORK_RESPONSE;
|
|
OrdinaryExchange->Status = STATUS_INVALID_NETWORK_RESPONSE;
|
|
} else {
|
|
OEM_STRING OemString;
|
|
UNICODE_STRING UnicodeString;
|
|
PQUERY_PRINT_JOB_INFO OutputBuffer = Irp->UserBuffer;
|
|
PSMB_RESP_PRINT_JOB_ID RespPrintJobId = (PSMB_RESP_PRINT_JOB_ID)((PUCHAR)Response+(Response->DataOffset-sizeof(SMB_HEADER)));
|
|
|
|
OutputBuffer->JobId = RespPrintJobId->JobId;
|
|
RtlInitAnsiString(&OemString, RespPrintJobId->ServerName);
|
|
UnicodeString.Buffer = OutputBuffer->ServerName;
|
|
UnicodeString.MaximumLength = sizeof(OutputBuffer->ServerName);
|
|
|
|
Status = RtlOemStringToUnicodeString(&UnicodeString, &OemString, FALSE);
|
|
|
|
if (Status == STATUS_SUCCESS) {
|
|
RtlInitAnsiString(&OemString, RespPrintJobId->QueueName);
|
|
UnicodeString.Buffer = OutputBuffer->QueueName;
|
|
UnicodeString.MaximumLength = sizeof(OutputBuffer->QueueName);
|
|
Status = RtlOemStringToUnicodeString(&UnicodeString, &OemString, FALSE);
|
|
|
|
IrpSp->Parameters.FileSystemControl.InputBufferLength = sizeof(QUERY_PRINT_JOB_INFO);
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
ASSERT(FALSE);
|
|
Status = STATUS_INVALID_NETWORK_RESPONSE;
|
|
OrdinaryExchange->Status = STATUS_INVALID_NETWORK_RESPONSE;
|
|
}
|
|
|
|
RxDbgTrace(-1, Dbg, ("MRxSmbFinishCoreIoCtl returning %08lx\n", Status ));
|
|
return Status;
|
|
}
|
|
|
|
typedef struct _SecPkgContext_TargetInformation
|
|
{
|
|
unsigned long MarshalledTargetInfoLength;
|
|
unsigned char SEC_FAR * MarshalledTargetInfo;
|
|
} SecPkgContext_TargetInformation, SEC_FAR * PSecPkgContext_TargetInformation;
|
|
|
|
NTSTATUS
|
|
MRxSmbQueryTargetInfo(
|
|
PRX_CONTEXT RxContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine performs a query target information operation against a connection
|
|
|
|
Arguments:
|
|
|
|
RxContext - the RDBSS context
|
|
|
|
Return Value:
|
|
|
|
RXSTATUS - The return status for the operation
|
|
|
|
Notes:
|
|
|
|
RDR gets the target information based on the security context of the connection and
|
|
returns the marshalled target information on the output buffer.
|
|
|
|
--*/
|
|
{
|
|
PMRX_V_NET_ROOT pVNetRoot = NULL;
|
|
PSMBCE_V_NET_ROOT_CONTEXT pVNetRootContext = NULL;
|
|
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
SECURITY_STATUS SecStatus;
|
|
|
|
SecPkgContext_TargetInformation SecTargetInfo;
|
|
|
|
PLOWIO_CONTEXT pLowIoContext = &RxContext->LowIoContext;
|
|
ULONG FsControlCode = pLowIoContext->ParamsFor.FsCtl.FsControlCode;
|
|
|
|
PLMR_QUERY_TARGET_INFO LmrQueryTargetInfo = RxContext->CurrentIrp->UserBuffer;
|
|
|
|
PAGED_CODE();
|
|
|
|
RxDbgTrace(+1, Dbg, ("MRxSmbQueryTargetInfo...\n", 0));
|
|
RxDbgTrace( 0, Dbg, ("MRxSmbQueryTargetInfo = %08lx\n", FsControlCode));
|
|
|
|
if (RxContext->CurrentIrp->RequestorMode != KernelMode) {
|
|
// this FSCTLS is only supported from a kernel mode component
|
|
return STATUS_INVALID_DEVICE_REQUEST;
|
|
}
|
|
|
|
if (LmrQueryTargetInfo == NULL) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (RxContext->pRelevantSrvOpen == NULL) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
pVNetRoot = (PMRX_V_NET_ROOT)RxContext->pRelevantSrvOpen->pVNetRoot;
|
|
|
|
if (NodeType(pVNetRoot) != RDBSS_NTC_V_NETROOT) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
pVNetRootContext = (PSMBCE_V_NET_ROOT_CONTEXT)pVNetRoot->Context;
|
|
|
|
SecStatus = QueryContextAttributesW(
|
|
&pVNetRootContext->pSessionEntry->Session.SecurityContextHandle,
|
|
SECPKG_ATTR_TARGET_INFORMATION,
|
|
&SecTargetInfo);
|
|
|
|
Status = MapSecurityError( SecStatus );
|
|
|
|
if (Status == STATUS_SUCCESS) {
|
|
if (SecTargetInfo.MarshalledTargetInfoLength+sizeof(LMR_QUERY_TARGET_INFO) > LmrQueryTargetInfo->BufferLength) {
|
|
LmrQueryTargetInfo->BufferLength = SecTargetInfo.MarshalledTargetInfoLength + sizeof(LMR_QUERY_TARGET_INFO);
|
|
Status = STATUS_BUFFER_TOO_SMALL;
|
|
} else {
|
|
RtlCopyMemory(LmrQueryTargetInfo->TargetInfoMarshalled,
|
|
SecTargetInfo.MarshalledTargetInfo,
|
|
SecTargetInfo.MarshalledTargetInfoLength);
|
|
|
|
LmrQueryTargetInfo->BufferLength = SecTargetInfo.MarshalledTargetInfoLength;
|
|
}
|
|
|
|
{
|
|
SIZE_T MarshalledTargetInfoLength_SizeT;
|
|
|
|
MarshalledTargetInfoLength_SizeT = SecTargetInfo.MarshalledTargetInfoLength;
|
|
|
|
ZwFreeVirtualMemory(
|
|
NtCurrentProcess(),
|
|
&SecTargetInfo.MarshalledTargetInfo,
|
|
&MarshalledTargetInfoLength_SizeT,
|
|
MEM_RELEASE);
|
|
|
|
ASSERT(MarshalledTargetInfoLength_SizeT <= MAXULONG);
|
|
SecTargetInfo.MarshalledTargetInfoLength = (ULONG)MarshalledTargetInfoLength_SizeT;
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
MRxSmbQueryRemoteServerName(
|
|
PRX_CONTEXT RxContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine performs a query remote file information operation against a srvopen
|
|
|
|
Arguments:
|
|
|
|
RxContext - the RDBSS context
|
|
|
|
Return Value:
|
|
|
|
RXSTATUS - The return status for the operation
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
PQUERY_REMOTE_SERVER_NAME info;
|
|
PLOWIO_CONTEXT pLowIoContext = &RxContext->LowIoContext;
|
|
RxCaptureFcb; RxCaptureFobx;
|
|
PMRX_SRV_OPEN SrvOpen = RxContext->pRelevantSrvOpen;
|
|
PMRX_SMB_SRV_OPEN smbSrvOpen;
|
|
PUNICODE_STRING s;
|
|
ULONG len;
|
|
|
|
PAGED_CODE();
|
|
|
|
if (SrvOpen == NULL || capFcb->pNetRoot == NULL || capFcb->pNetRoot->pSrvCall == NULL) {
|
|
return STATUS_NOT_SUPPORTED;
|
|
}
|
|
smbSrvOpen = MRxSmbGetSrvOpenExtension(SrvOpen);
|
|
|
|
info = (PQUERY_REMOTE_SERVER_NAME) pLowIoContext->ParamsFor.FsCtl.pOutputBuffer;
|
|
if (pLowIoContext->ParamsFor.FsCtl.OutputBufferLength < sizeof(QUERY_REMOTE_SERVER_NAME)) {
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
// we get the srvname
|
|
len = 0;
|
|
s = capFcb->pNetRoot->pSrvCall->pSrvCallName;
|
|
if (s != NULL) {
|
|
len += s->Length;
|
|
}
|
|
s = capFcb->pNetRoot->pSrvCall->pDomainName;
|
|
if (s != NULL) {
|
|
len += s->Length;
|
|
}
|
|
|
|
info->ServerNameLength = len;
|
|
RxContext->InformationToReturn = sizeof(QUERY_REMOTE_SERVER_NAME);
|
|
if (pLowIoContext->ParamsFor.FsCtl.OutputBufferLength < sizeof(QUERY_REMOTE_SERVER_NAME) + len) {
|
|
info->ServerName[0] = L'\0';
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
len = 0;
|
|
s = capFcb->pNetRoot->pSrvCall->pSrvCallName;
|
|
if (s != NULL) {
|
|
RtlCopyMemory(info->ServerName, s->Buffer+1, s->Length-sizeof(WCHAR));
|
|
len += s->Length - sizeof(WCHAR);
|
|
}
|
|
s = capFcb->pNetRoot->pSrvCall->pDomainName;
|
|
if (s != NULL) {
|
|
info->ServerName[len / sizeof(WCHAR)] = L'.';
|
|
len += sizeof(WCHAR);
|
|
RtlCopyMemory(&info->ServerName[len / sizeof(WCHAR)], s->Buffer, s->Length);
|
|
len += s->Length;
|
|
}
|
|
|
|
RxContext->InformationToReturn += len;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|