/*++ Copyright (c) 1989 Microsoft Corporation Module Name: read.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: --*/ #include "precomp.h" #pragma hdrstop #include // // The local debug trace level // RXDT_DefineCategory(FSCTRL); #define Dbg (DEBUG_TRACE_FSCTRL) NTSTATUS MRxProxyFsCtl( 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 = STATUS_INVALID_DEVICE_REQUEST; PLOWIO_CONTEXT pLowIoContext = &RxContext->LowIoContext; ULONG FsControlCode = pLowIoContext->ParamsFor.FsCtl.FsControlCode; RxDbgTrace(+1, Dbg, ("MRxProxyFsCtl...\n", 0)); RxDbgTrace( 0, Dbg, ("MRxProxyFsCtl = %08lx\n", FsControlCode)); switch (pLowIoContext->ParamsFor.FsCtl.MinorFunction) { case IRP_MN_USER_FS_REQUEST: 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 : case FSCTL_MAILSLOT_PEEK : case FSCTL_DFS_GET_REFERRALS: case FSCTL_DFS_REPORT_INCONSISTENCY: case FSCTL_LMR_TRANSACT : default: // Status = MRxProxyFsControl(RxContext); // Temporarily stubbed out till the buffer passing strategy has been // finalized. Status = STATUS_NOT_IMPLEMENTED; break; } break; default : break; } RxDbgTrace(-1, Dbg, ("MRxProxyFsCtl -> %08lx\n", Status )); return Status; } typedef struct _PROXY_NOTIFY_CHANGE_DIRECTORY_CONTEXT_ { PRX_CONTEXT pRxContext; //REQ_NOTIFY_CHANGE NotifyRequest; //PROXY_TRANSACTION_OPTIONS Options; //PROXY_TRANSACTION_RESUMPTION_CONTEXT ResumptionContext; } PROXY_NOTIFY_CHANGE_DIRECTORY_CONTEXT, *PPROXY_NOTIFY_CHANGE_DIRECTORY_CONTEXT; VOID MRxProxyNotifyChangeDirectoryCompletion( PPROXY_NOTIFY_CHANGE_DIRECTORY_CONTEXT pNotificationContext) /*++ Routine Description: This routine is invokde 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 wrapped. In cases os successful directory change notification completion the appropriate completion routine is invoked and the RxContext modified to prevent any cancellation from proceeding further. --*/ { NTSTATUS Status; PRX_CONTEXT pRxContext; PMRXPROXY_RX_CONTEXT pMRxProxyContext; //PPROXY_EXCHANGE pExchange = NULL; ProxyAcquireGlobalSpinLock(); pRxContext = pNotificationContext->pRxContext; if (pRxContext != NULL) { // This is a case of successful completion of the change directory // notification, i.e., the request was not cancelled. In such cases // prevent all race conditions by modifying the RxContext under lock // to turn back cancellation request. pMRxProxyContext = MRxProxyGetMinirdrContext(pRxContext); //pExchange = pMRxProxyContext->pExchange; pMRxProxyContext->pCancelContext = NULL; pNotificationContext->pRxContext = NULL; } ProxyReleaseGlobalSpinLock(); // Complete the Context if it was not previously cancelled if (pRxContext != NULL) { //PPROXY_TRANSACTION_RESUMPTION_CONTEXT pResumptionContext; //pResumptionContext = &(pNotificationContext->ResumptionContext); //pRxContext->StoredStatus = pResumptionContext->FinalStatusFromServer; Status = RxSetMinirdrCancelRoutine(pRxContext,NULL); if (Status == STATUS_SUCCESS) { RxLowIoCompletion(pRxContext); } } #if 0 // Free the associated exchange. if (pExchange != NULL) { ProxyCeDereferenceAndDiscardExchange(pExchange); } #endif //0 // Free the notification context. RxFreePool(pNotificationContext); } NTSTATUS MRxProxyNotifyChangeDirectoryCancellation( PRX_CONTEXT RxContext) /*++ Routine Description: This routine is invokde when a directory change notification operation is cancelled. Arguments: RxContext - the RDBSS context Return Value: NTSTATUS - The return status for the operation --*/ { //NTSTATUS Status; BOOLEAN ChangeDirectoryNotificationCompleted; PMRXPROXY_RX_CONTEXT pMRxProxyContext = MRxProxyGetMinirdrContext(RxContext); //PPROXY_EXCHANGE pExchange; PPROXY_NOTIFY_CHANGE_DIRECTORY_CONTEXT pNotificationContext; ProxyAcquireGlobalSpinLock(); pNotificationContext = (PPROXY_NOTIFY_CHANGE_DIRECTORY_CONTEXT)pMRxProxyContext->pCancelContext; ChangeDirectoryNotificationCompleted = (pNotificationContext == NULL); if (!ChangeDirectoryNotificationCompleted) { // This is a case of successful cancellation of the change directory // notification. In such cases prevent all race conditions by modifying // the RxContext under lock to prevent successful completion pNotificationContext->pRxContext = NULL; //pExchange = pMRxProxyContext->pExchange; pMRxProxyContext->pCancelContext = NULL; } ProxyReleaseGlobalSpinLock(); if (ChangeDirectoryNotificationCompleted) { // The cancellation is trivial since the request has already been completed return STATUS_SUCCESS; } #if 0 //can't do any of this....no exchanges......... if (pExchange != NULL) { UCHAR LastCommandInHeader; PUCHAR pCommand; BYTE CancelRequestBuffer[sizeof(PROXY_HEADER) + sizeof(REQ_NT_CANCEL)]; ULONG CancelRequestBufferSize = sizeof(CancelRequestBuffer); // Build the Cancel request and send it across to the server. Status = ProxyCeBuildProxyHeader( pExchange, CancelRequestBuffer, CancelRequestBufferSize, &CancelRequestBufferSize, &LastCommandInHeader, &pCommand); ASSERT(LastCommandInHeader == PROXY_COM_NO_ANDX_COMMAND); *pCommand = PROXY_COM_NT_CANCEL; if (Status == RX_MAP_STATUS(SUCCESS)) { PREQ_NT_CANCEL pCancelRequest = (PREQ_NT_CANCEL)(&CancelRequestBuffer[sizeof(PROXY_HEADER)]); PMDL pCancelProxyMdl; ProxyPutUshort(&pCancelRequest->WordCount,0); pCancelRequest->ByteCount = 0; CancelRequestBufferSize = sizeof(CancelRequestBuffer); pCancelProxyMdl = RxAllocateMdl(CancelRequestBuffer,CancelRequestBufferSize); if (pCancelProxyMdl != NULL) { RxProbeAndLockPages(pCancelProxyMdl,KernelMode,IoModifyAccess,Status); if (Status == STATUS_SUCCESS) { Status = ProxyCeSend( pExchange, RXCE_SEND_SYNCHRONOUS, pCancelProxyMdl, CancelRequestBufferSize); MmUnlockPages(pCancelProxyMdl); } IoFreeMdl(pCancelProxyMdl); } } ProxyCeDereferenceAndDiscardExchange(pExchange); } #endif //0 // Complete the request. RxContext->StoredStatus = STATUS_CANCELLED; RxLowIoCompletion(RxContext); return STATUS_SUCCESS; } NTSTATUS MRxProxyNotifyChangeDirectory( 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: THIS STUFF IS WRONG!!!!! A directory change notification opertaion is an asychronous operation. It consists of sending a PROXY 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 PROXY 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 PROXY 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; PLOWIO_CONTEXT pLowIoContext = &RxContext->LowIoContext; //PPROXYCEDB_SERVER_ENTRY pServerEntry; //PPROXY_NOTIFY_CHANGE_DIRECTORY_CONTEXT pNotificationContext; #if 0 PBYTE pInputParamBuffer = NULL; PBYTE pOutputParamBuffer = NULL; PBYTE pInputDataBuffer = NULL; PBYTE pOutputDataBuffer = NULL; ULONG InputParamBufferLength = 0; ULONG OutputParamBufferLength = 0; ULONG InputDataBufferLength = 0; ULONG OutputDataBufferLength = 0; #endif //0 RxDbgTrace(+1, Dbg, ("MRxNotifyChangeDirectory...Entry\n", 0)); //pServerEntry = ProxyCeGetAssociatedServerEntry(capFcb->pNetRoot->pSrvCall); //if (!FlagOn(pServerEntry->Server.DialectFlags,DF_NT_PROXYS)) { // return STATUS_NOT_SUPPORTED; //} #if 0 pNotificationContext = (PPROXY_NOTIFY_CHANGE_DIRECTORY_CONTEXT) RxAllocatePoolWithTag( NonPagedPool, sizeof(PROXY_NOTIFY_CHANGE_DIRECTORY_CONTEXT), MRXPROXY_FSCTL_POOLTAG); if (pNotificationContext != NULL) { PREQ_NOTIFY_CHANGE pNotifyRequest; PPROXY_TRANSACTION_OPTIONS pTransactionOptions; PPROXY_TRANSACTION_RESUMPTION_CONTEXT pResumptionContext; PMRX_PROXY_SRV_OPEN pProxySrvOpen; RxCaptureFobx; ASSERT (capFobx != NULL); Status = MRxProxyDeferredCreate(RxContext); if (Status==STATUS_SUCCESS) { pProxySrvOpen = MRxProxyGetSrvOpenExtension(capFobx->pSrvOpen); ASSERT (!FlagOn(pProxySrvOpen->Flags,PROXY_SRVOPEN_FLAG_NOT_REALLY_OPEN)); pNotificationContext->pRxContext = RxContext; pNotifyRequest = &(pNotificationContext->NotifyRequest); pTransactionOptions = &(pNotificationContext->Options); pResumptionContext = &(pNotificationContext->ResumptionContext); pNotifyRequest->CompletionFilter = pLowIoContext->ParamsFor.NotifyChangeDirectory.CompletionFilter; pNotifyRequest->Fid = pProxySrvOpen->Fid; pNotifyRequest->WatchTree = pLowIoContext->ParamsFor.NotifyChangeDirectory.WatchTree; pNotifyRequest->Reserved = 0; OutputDataBufferLength = pLowIoContext->ParamsFor.NotifyChangeDirectory.NotificationBufferLength; pOutputDataBuffer = pLowIoContext->ParamsFor.NotifyChangeDirectory.pNotificationBuffer; *pTransactionOptions = RxDefaultTransactionOptions; pTransactionOptions->NtTransactFunction = NT_TRANSACT_NOTIFY_CHANGE; pTransactionOptions->TimeoutIntervalInMilliSeconds = PROXYCE_TRANSACTION_TIMEOUT_NOT_USED; pTransactionOptions->Flags = PROXY_XACT_FLAGS_INDEFINITE_DELAY_IN_RESPONSE; ProxyCeInitializeAsynchronousTransactionResumptionContext( pResumptionContext,MRxProxyNotifyChangeDirectoryCompletion,pNotificationContext); Status = ProxyCeAsynchronousTransact( RxContext, // the RXContext for the transaction pTransactionOptions, // transaction options pNotifyRequest, // the setup buffer sizeof(REQ_NOTIFY_CHANGE), // setup buffer length 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) { PMRXPROXY_RX_CONTEXT pMRxProxyContext; pMRxProxyContext = MRxProxyGetMinirdrContext(RxContext); pMRxProxyContext->pCancelContext = pNotificationContext; // Ensure that the appropriate cancel routine is set because this is a long term // operation and the cancelling mechanism needs to be in place. Status = RxSetMinirdrCancelRoutine(RxContext,MRxProxyNotifyChangeDirectoryCancellation); if (Status == STATUS_SUCCESS) { Status = STATUS_PENDING; } else if (Status == STATUS_CANCELLED) { MRxProxyNotifyChangeDirectoryCancellation(RxContext); Status = STATUS_PENDING; } } else { // On exit from this routine the request would have been completed in all // the cases. The asynchronous case and synchronous case are folded into // one async response by returning STATUS_PENDING. Status = STATUS_PENDING; } } else { NOTHING; //just return the status from the deferred open call } } else { Status = STATUS_INSUFFICIENT_RESOURCES; } #endif //0 Status = STATUS_NOT_SUPPORTED; RxDbgTrace(-1, Dbg, ("MRxProxyNotifyChangeDirectory -> %08lx\n", Status )); return Status; } NTSTATUS MRxProxyFsControl(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; RxCaptureFobx; //PMRX_PROXY_SRV_OPEN pProxySrvOpen; PLOWIO_CONTEXT pLowIoContext = &RxContext->LowIoContext; ULONG FsControlCode = pLowIoContext->ParamsFor.FsCtl.FsControlCode; PAGED_CODE(); RxDbgTrace(+1, Dbg, ("MRxProxyFsControl...Entry FsControlCode(%lx)\n", FsControlCode)); Status = STATUS_NOT_IMPLEMENTED; RxDbgTrace(-1, Dbg, ("MRxProxyFsControl...Exit\n")); return Status; } #if DBG NTSTATUS MRxProxyTestForLowIoIoctl( 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 = &capFcb->AlreadyPrefixedName; ULONG ReturnLength; ReturnLength = OutputBufferLength; if (ReturnLength > FileName->Length) { ReturnLength = FileName->Length; } RxDbgTrace(0, Dbg, ("Here in MRxProxyTestForLowIoIoctl %s, obl = %08lx, rl=%08lx\n", Buffer, OutputBufferLength, ReturnLength)); //return an obvious string to make sure that darryl is copying the results out correctly //BUGBUG 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 MRxProxyIoCtl( 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; RxDbgTrace(+1, Dbg, ("MRxProxyIoCtl...\n", 0)); RxDbgTrace( 0, Dbg, ("MRxProxyIoCtl = %08lx\n", IoControlCode)); switch (IoControlCode) { #if DBG case IOCTL_LMMR_TESTLOWIO: Status = MRxProxyTestForLowIoIoctl(RxContext); break; #endif //if DBG default: break; } RxDbgTrace(-1, Dbg, ("MRxProxyIoCtl -> %08lx\n", Status )); return Status; }