/*++

Copyright (c) 1989  Microsoft Corporation

Module Name:

    FsCtrl.c

Abstract:

    This module implements the File System Control routines for Rdbss. Fsctls on the device
    fcb are handled in another module.

Author:

   Joe Linn [JoeLinn] 7-mar-95

Revision History:

   Balan Sethu Raman 18-May-95 -- Integrated with mini rdrs

--*/

#include "precomp.h"
#pragma hdrstop
#include <dfsfsctl.h>

//  The local debug trace level

#define Dbg                              (DEBUG_TRACE_FSCTRL)


//  Local procedure prototypes

NTSTATUS
RxUserFsCtrl ( RXCOMMON_SIGNATURE );


#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, RxCommonFileSystemControl)
#pragma alloc_text(PAGE, RxUserFsCtrl)
#pragma alloc_text(PAGE, RxLowIoFsCtlShell)
#pragma alloc_text(PAGE, RxLowIoFsCtlShellCompletion)
#endif

NTSTATUS
RxCommonFileSystemControl ( RXCOMMON_SIGNATURE )
/*++

Routine Description:

    This is the common routine for doing FileSystem control operations called
    by both the fsd and fsp threads. What happens is that we pick off fsctls that we know about
    and remote the rest....remoting means sending them thru the lowio stuff which may/will pick off
    a few more. the ones that we pick off here (and currently return STATUS_NOT_IMPLEMENTED) and the
    ones for being an oplock provider and for doing volume mounts....we don't even have volume fcbs
    yet since this is primarily a localFS concept. nevertheless, these are not passed thru to the mini.

Arguments:


Return Value:

    RXSTATUS - The return status for the operation
--*/
{
   RxCaptureRequestPacket;
   RxCaptureParamBlock;
   RxCaptureFcb;
   RxCaptureFobx;
   RxCaptureFileObject;

   NTSTATUS       Status;
   NODE_TYPE_CODE TypeOfOpen;
   BOOLEAN TryLowIo = TRUE;
   ULONG FsControlCode = capPARAMS->Parameters.FileSystemControl.FsControlCode;

    PAGED_CODE();

   RxDbgTrace(+1, Dbg, ("RxCommonFileSystemControl %08lx\n", RxContext));
   RxDbgTrace( 0, Dbg, ("Irp           = %08lx\n", capReqPacket));
   RxDbgTrace( 0, Dbg, ("MinorFunction = %08lx\n", capPARAMS->MinorFunction));
   RxDbgTrace( 0, Dbg, ("FsControlCode = %08lx\n", FsControlCode));

   RxLog(("FsCtl %x %x %x %x",RxContext,capReqPacket,capPARAMS->MinorFunction,FsControlCode));

   ASSERT(capPARAMS->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL);

   switch (capPARAMS->MinorFunction) {
   case IRP_MN_USER_FS_REQUEST:
      {

         RxDbgTrace( 0, Dbg, ("FsControlCode = %08lx\n", FsControlCode));
         switch (FsControlCode) {
         case FSCTL_REQUEST_OPLOCK_LEVEL_1:
         case FSCTL_REQUEST_OPLOCK_LEVEL_2:
         case FSCTL_REQUEST_BATCH_OPLOCK:
         case FSCTL_OPLOCK_BREAK_ACKNOWLEDGE:
         case FSCTL_OPBATCH_ACK_CLOSE_PENDING:
         case FSCTL_OPLOCK_BREAK_NOTIFY:
         case FSCTL_OPLOCK_BREAK_ACK_NO_2:
            {
               // fsrtl oplock package is handled in common for all minirdrs

               //Status = RxOplockRequest( RXCOMMON_ARGUMENTS, &PostToFsp );
               Status = STATUS_NOT_IMPLEMENTED;
               TryLowIo = FALSE;
            }
            break;

         case FSCTL_LOCK_VOLUME:
         case FSCTL_UNLOCK_VOLUME:
         case FSCTL_DISMOUNT_VOLUME:
         case FSCTL_MARK_VOLUME_DIRTY:
         case FSCTL_IS_VOLUME_MOUNTED:
            {
               //  Decode the file object, the only type of opens we accept are
               //  user volume opens.
               TypeOfOpen    = NodeType(capFcb);

               if (TypeOfOpen != RDBSS_NTC_VOLUME_FCB) {
                   Status = STATUS_INVALID_PARAMETER;
               } else {
                   //Status = RxFsdPostRequestWithResume(RxContext,RxCommonDevFCBFsCtl);
                   Status = STATUS_NOT_IMPLEMENTED;
               }
               TryLowIo = FALSE;
            }
            break;
         case FSCTL_DFS_GET_REFERRALS:
         case FSCTL_DFS_REPORT_INCONSISTENCY:
            {
               if (!BooleanFlagOn(capFcb->pVNetRoot->pNetRoot->pSrvCall->Flags,SRVCALL_FLAG_DFS_AWARE_SERVER)) {
                  TryLowIo = FALSE;
                  Status = STATUS_DFS_UNAVAILABLE;
               }
            }
            break;
         default:
            break;
         }
      }
      break;
    default:
       break;
    }

    if (TryLowIo) {
        Status = RxLowIoFsCtlShell(RxContext);
    }

    if (RxContext->PostRequest) {
       Status = RxFsdPostRequest(RxContext);
    }

   RxDbgTrace(-1, Dbg, ("RxCommonFileSystemControl -> %08lx\n", Status));

   return Status;
}

ULONG RxEnablePeekBackoff = 1;

NTSTATUS
RxLowIoFsCtlShell( RXCOMMON_SIGNATURE )
/*++

Routine Description:

    This is the common routine for implementing the user's requests made
    through NtFsControlFile.

Arguments:

    Irp - Supplies the Irp being processed

Return Value:

    RXSTATUS - The return status for the operation

--*/

{
    RxCaptureFcb;
    RxCaptureRequestPacket;
    RxCaptureParamBlock;
    RxCaptureFobx;
    RxCaptureFileObject;

    NTSTATUS       Status        = STATUS_SUCCESS;
    BOOLEAN        PostToFsp     = FALSE;
    //BOOLEAN        TryToRemoteIt = TRUE;
    NODE_TYPE_CODE TypeOfOpen    = NodeType(capFcb);
    PLOWIO_CONTEXT pLowIoContext  = &RxContext->LowIoContext;
    ULONG          FsControlCode = capPARAMS->Parameters.FileSystemControl.FsControlCode;
    BOOLEAN        SubmitLowIoRequest = TRUE;

    PAGED_CODE();

    RxDbgTrace(+1, Dbg, ("RxLowIoFsCtlShell...\n", 0));
    RxDbgTrace( 0, Dbg, ("FsControlCode = %08lx\n", FsControlCode));

    RxInitializeLowIoContext(pLowIoContext,LOWIO_OP_FSCTL);

    switch (capPARAMS->MinorFunction) {
    case IRP_MN_USER_FS_REQUEST:
        {
        //  The RDBSS filters out those FsCtls that can be handled without the intervention
        //  of the mini rdr's. Currently all FsCtls are forwarded down to the mini rdr.
        switch (FsControlCode) {
        case FSCTL_PIPE_PEEK:
           {
              if (RxShouldRequestBeThrottled(&capFobx->Specific.NamedPipe.ThrottlingState)
                       && RxEnablePeekBackoff) {
                    PFILE_PIPE_PEEK_BUFFER pPeekBuffer;

                    ASSERT(capReqPacket->UserBuffer != NULL);

                    pPeekBuffer = (PFILE_PIPE_PEEK_BUFFER)capReqPacket->UserBuffer;

                    SubmitLowIoRequest = FALSE;
                    RxDbgTrace(0, (DEBUG_TRACE_ALWAYS), ("RxLowIoFsCtlShell: Throttling Peek Request\n"));

                    capReqPacket->IoStatus.Information = FIELD_OFFSET(FILE_PIPE_PEEK_BUFFER,Data);
                    pPeekBuffer->ReadDataAvailable = 0;
                    pPeekBuffer->NamedPipeState    = FILE_PIPE_CONNECTED_STATE;
                    pPeekBuffer->NumberOfMessages  = MAXULONG;
                    pPeekBuffer->MessageLength     = 0;

                    Status = STATUS_SUCCESS;
                    RxContext->StoredStatus = Status;
              } else {

                  RxDbgTrace(0, (DEBUG_TRACE_ALWAYS), ("RxLowIoFsCtlShell: Throttling queries %ld\n",
                                       capFobx->Specific.NamedPipe.ThrottlingState.NumberOfQueries));

                  RxLog(("ThrottlQs %lx %lx %lx %ld\n",
                                       RxContext,capFobx,&capFobx->Specific.NamedPipe.ThrottlingState,
                                       capFobx->Specific.NamedPipe.ThrottlingState.NumberOfQueries));

              }


           }
           break;
        default:
           break;
        } //end of the inner switch
        } //end of the case
        break;
    default:
        break;
    }

    if (SubmitLowIoRequest) {
       Status = RxLowIoSubmit(RxContext,RxLowIoFsCtlShellCompletion);
    }

    RxDbgTrace(-1, Dbg, ("RxLowIoFsCtlShell -> %08lx\n", Status ));
    return Status;
}

NTSTATUS
RxLowIoFsCtlShellCompletion( RXCOMMON_SIGNATURE )
/*++

Routine Description:

    This is the completion routine for FSCTL requests passed down to the mini rdr

Arguments:

    Irp - Supplies the Irp being processed

Return Value:

    RXSTATUS - The return status for the operation

--*/

{
    RxCaptureRequestPacket;
    RxCaptureFcb;
    RxCaptureFobx;

    NTSTATUS       Status;
    //NODE_TYPE_CODE TypeOfOpen    = NodeType(capFcb);
    PLOWIO_CONTEXT pLowIoContext  = &RxContext->LowIoContext;
    ULONG          FsControlCode = pLowIoContext->ParamsFor.FsCtl.FsControlCode;

    PAGED_CODE();

    Status = RxContext->StoredStatus;
    RxDbgTrace(+1, Dbg, ("RxLowIoFsCtlShellCompletion  entry  Status = %08lx\n", Status));

    switch (FsControlCode) {
    case FSCTL_PIPE_PEEK:
       {
          if ((Status == STATUS_SUCCESS) ||
              (Status == STATUS_BUFFER_OVERFLOW)) {
             // In the case of Peek operations a throttle mechanism is in place to
             // prevent the network from being flodded with requests which return 0
             // bytes.

             PFILE_PIPE_PEEK_BUFFER pPeekBuffer;

             pPeekBuffer = (PFILE_PIPE_PEEK_BUFFER)pLowIoContext->ParamsFor.FsCtl.pOutputBuffer;

             if (pPeekBuffer->ReadDataAvailable == 0) {

                 // The peek request returned zero bytes.

                 RxDbgTrace(0, (DEBUG_TRACE_ALWAYS), ("RxLowIoFsCtlShellCompletion: Enabling Throttling for Peek Request\n"));
                 RxInitiateOrContinueThrottling(&capFobx->Specific.NamedPipe.ThrottlingState);
                 RxLog(("ThrottlYes %lx %lx %lx %ld\n",
                                   RxContext,capFobx,&capFobx->Specific.NamedPipe.ThrottlingState,
                                   capFobx->Specific.NamedPipe.ThrottlingState.NumberOfQueries));

             } else {

                 RxDbgTrace(0, (DEBUG_TRACE_ALWAYS), ("RxLowIoFsCtlShellCompletion: Disabling Throttling for Peek Request\n"));
                 RxTerminateThrottling(&capFobx->Specific.NamedPipe.ThrottlingState);
                 RxLog(("ThrottlNo %lx %lx %lx %ld\n",
                                   RxContext,capFobx,&capFobx->Specific.NamedPipe.ThrottlingState,
                                   capFobx->Specific.NamedPipe.ThrottlingState.NumberOfQueries));
             }

             capReqPacket->IoStatus.Information = RxContext->InformationToReturn;
          }
       }
       break;
    default:
       if ((Status == STATUS_BUFFER_OVERFLOW) ||
           (Status == STATUS_SUCCESS)) {
          //ASSERT( RxContext->InformationToReturn == pLowIoContext->ParamsFor.FsCtl.OutputBufferLength);
          //capReqPacket->IoStatus.Information = pLowIoContext->ParamsFor.FsCtl.OutputBufferLength;
          capReqPacket->IoStatus.Information = RxContext->InformationToReturn;
       }
       break;
    }

    capReqPacket->IoStatus.Status = Status;
    RxDbgTrace(-1, Dbg, ("RxLowIoFsCtlShellCompletion  exit  Status = %08lx\n", Status));
    return Status;
}

//
//  Local support routine
//

//OPLOCKS we will want this eventually....don't take it out
//        there are embedded #ifs so it is not trivial to do this
#if 0
NTSTATUS
RxOplockRequest (
                  RXCOMMON_SIGNATURE,
                  IN PBOOLEAN PostToFsp
                )
/*++

Routine Description:

    This is the common routine to handle oplock requests made via the
    NtFsControlFile call.

Arguments:

    Irp - Supplies the Irp being processed

Return Value:

    RXSTATUS - The return status for the operation

--*/
{
    NTSTATUS Status = STATUS_SUCCESS;
    RxCaptureRequestPacket;
    RxCaptureFcb; RxCaptureFobx; RxCaptureParamBlock;
    NODE_TYPE_CODE TypeOfOpen = NodeType(capFcb);
    ULONG FsControlCode = capPARAMS->Parameters.FileSystemControl.FsControlCode;

    ULONG OplockCount;

    BOOLEAN AcquiredVcb = FALSE;

    PAGED_CODE();

    RxDbgTrace(+1, Dbg, ("RxOplockRequest...\n", 0));
    RxDbgTrace( 0, Dbg, ("FsControlCode = %08lx\n", FsControlCode));

    //
    //  We only permit oplock requests on files.
    //

    if ( TypeOfOpen != RDBSS_NTC_STORAGE_TYPE_FILE ) {
        //RxCompleteRequest( RxContext, RxStatus(INVALID_PARAMETER) );
        RxDbgTrace(-1, Dbg, ("RxOplockRequest -> RxStatus(INVALID_PARAMETER\n)", 0));
        return STATUS_INVALID_PARAMETER;
    }

    //
    //  Switch on the function control code.  We grab the Fcb exclusively
    //  for oplock requests, shared for oplock break acknowledgement.
    //

    switch ( FsControlCode ) {

    case FSCTL_REQUEST_OPLOCK_LEVEL_1:
    case FSCTL_REQUEST_OPLOCK_LEVEL_2:
    case FSCTL_REQUEST_BATCH_OPLOCK:

        //joejoe don't be an oplock filesystem yet
        RxCompleteContextAndReturn( STATUS_OPLOCK_NOT_GRANTED );
//BUGBUG move this code down to wrapper.sav
//BUGBUG should we return NOT_IMPLEMENTED instead???
//i removed this code by #if so that we can see what we'll have to put in for oplocks
#if 0
        if ( !RxAcquireSharedVcb( RxContext, capFcb->Vcb )) {

            //
            //  If we can't acquire the Vcb, then this is an invalid
            //  operation since we can't post Oplock requests.
            //

            RxDbgTrace(0, Dbg, ("Cannot acquire exclusive Vcb\n", 0));

            //RxCompleteRequest( RxContext, RxStatus(OPLOCK_NOT_GRANTED) );
            RxDbgTrace(-1, Dbg, ("RxOplockRequest -> RxStatus(OPLOCK_NOT_GRANTED\n)", 0));
            return STATUS_OPLOCK_NOT_GRANTED;
        }

        AcquiredVcb = TRUE;

        //
        //  We set the wait parameter in the RxContext to FALSE.  If this
        //  request can't grab the Fcb and we are in the Fsp thread, then
        //  we fail this request.
        //

        ClearFlag(RxContext->Flags, RX_CONTEXT_FLAG_WAIT);

        if ( !RxAcquireExclusiveFcb( RxContext, capFcb )) {

            RxDbgTrace(0, Dbg, ("Cannot acquire exclusive Fcb\n", 0));

            RxReleaseVcb( RxContext, capFcb->Vcb );

            //
            //  We fail this request.
            //

            Status = STATUS_OPLOCK_NOT_GRANTED;

            //RxCompleteRequest( RxContext, Status );

            RxDbgTrace(-1, Dbg, ("RxOplockRequest -> %08lx\n", Status ));
            return Status;
        }

        if (FsControlCode == FSCTL_REQUEST_OPLOCK_LEVEL_2) {

            OplockCount = (ULONG) FsRtlAreThereCurrentFileLocks( &capFcb->Specific.Fcb.FileLock );

        } else {

            OplockCount = capFcb->UncleanCount;
        }

        break;
#endif
    case FSCTL_OPLOCK_BREAK_ACKNOWLEDGE:
    case FSCTL_OPBATCH_ACK_CLOSE_PENDING :
    case FSCTL_OPLOCK_BREAK_NOTIFY:
    case FSCTL_OPLOCK_BREAK_ACK_NO_2:

        //joejoe don't be an oplock filesystem yet
        RxCompleteContextAndReturn( STATUS_INVALID_OPLOCK_PROTOCOL );
#if 0
//just keep it for a model
        if ( !RxAcquireSharedFcb( RxContext, capFcb )) {

            RxDbgTrace(0, Dbg, ("Cannot acquire shared Fcb\n", 0));

            Status = RxFsdPostRequest( RxContext );

            RxDbgTrace(-1, Dbg, ("RxOplockRequest -> %08lx\n", Status ));
            return Status;
        }

        break;
#endif
    default:

        RxBugCheck( FsControlCode, 0, 0 );
    }

    //
    //  Use a try finally to free the Fcb.
    //

    try {

        ////
        ////  Call the FsRtl routine to grant/acknowledge oplock.
        ////
        //
        //Status = FsRtlOplockFsctrl( &capFcb->Specific.Fcb.Oplock,
        //                            capReqPacket,
        //                            OplockCount );

        //
        //  Set the flag indicating if Fast I/O is possible
        //

        capFcb->Header.IsFastIoPossible = RxIsFastIoPossible( capFcb );

    } finally {

        DebugUnwind( RxOplockRequest );

        //
        //  Release all of our resources
        //

        if (AcquiredVcb) {

            RxReleaseVcb( RxContext, capFcb->Vcb );
        }

        RxReleaseFcb( RxContext, capFcb );

        if (!AbnormalTermination()) {

            //RxCompleteRequest_OLD( RxContext, RxNull, 0 );
        }

        RxDbgTrace(-1, Dbg, ("RxOplockRequest -> %08lx\n", Status ));
    }

    return Status;
}
#endif

