/*++

Copyright (c) 1989 - 1999 Microsoft Corporation

Module Name:

    write.c

Abstract:

    This module implements the mini redirector call down routines pertaining
    to write of file system objects.

--*/

#include "precomp.h"
#pragma hdrstop
#pragma warning(error:4101)   // Unreferenced local variable

#ifdef  ALLOC_PRAGMA
#pragma alloc_text(PAGE, MRxSmbWrite)
#pragma alloc_text(PAGE, MRxSmbBuildWriteRequest)
#pragma alloc_text(PAGE, SmbPseExchangeStart_Write)
#pragma alloc_text(PAGE, MRxSmbFinishWrite)
#endif

#define MAX(a,b) ((a) > (b) ? (a) : (b))

//
//  The local debug trace level
//

#define Dbg                              (DEBUG_TRACE_WRITE)

#ifndef FORCE_NO_NTWRITEANDX
#define MRxSmbForceNoNtWriteAndX FALSE
#else
BOOLEAN MRxSmbForceNoNtWriteAndX = TRUE;
#endif

#define WRITE_COPY_THRESHOLD 64
#define FORCECOPYMODE FALSE

#ifdef SETFORCECOPYMODE
#undef  FORCECOPYMODE
#define FORCECOPYMODE MRxSmbForceCopyMode
ULONG MRxSmbForceCopyMode = TRUE;
#endif

NTSTATUS
SmbPseExchangeStart_Write(
    SMBPSE_ORDINARY_EXCHANGE_ARGUMENT_SIGNATURE
    );

ULONG MRxSmbWriteSendOptions = 0;

NTSTATUS
MRxSmbWrite (
    IN PRX_CONTEXT RxContext)
/*++

Routine Description:

   This routine opens a file across the network.

Arguments:

    RxContext - the RDBSS context

Return Value:

    NTSTATUS - The return status for the operation

--*/
{
    NTSTATUS Status = STATUS_SUCCESS;

    RxCaptureFcb;
    RxCaptureFobx;

    PMRX_SRV_OPEN SrvOpen;
    PMRX_SMB_SRV_OPEN smbSrvOpen;

    PSMB_PSE_ORDINARY_EXCHANGE OrdinaryExchange;

    PLOWIO_CONTEXT LowIoContext = &RxContext->LowIoContext;
    
    PAGED_CODE();

    RxDbgTrace(+1, Dbg, ("MRxSmbWrite\n", 0 ));

    if (RxContext->pFcb->pNetRoot->Type == NET_ROOT_PIPE) {
        Status = STATUS_NOT_SUPPORTED;

        RxDbgTrace(-1, Dbg, ("MRxSmbWrite: Pipe write returned %lx\n",Status));
        return Status;
    }

    if ( NodeType(capFcb) == RDBSS_NTC_MAILSLOT ) {

        Status = STATUS_NOT_SUPPORTED;

        RxDbgTrace(-1, Dbg, ("MRxSmbWrite: Mailslot write returned %lx\n",Status));
        return Status;
    }

    if(NodeType(capFcb) == RDBSS_NTC_STORAGE_TYPE_FILE) {
        PMRX_SMB_FCB smbFcb = MRxSmbGetFcbExtension(capFcb);
        smbFcb->MFlags |= SMB_FCB_FLAG_WRITES_PERFORMED;
    }

    ASSERT( NodeType(capFobx->pSrvOpen) == RDBSS_NTC_SRVOPEN );

    SrvOpen = capFobx->pSrvOpen;
    smbSrvOpen = MRxSmbGetSrvOpenExtension(SrvOpen);

    if (smbSrvOpen->OplockLevel == SMB_OPLOCK_LEVEL_II &&
        !BooleanFlagOn(LowIoContext->ParamsFor.ReadWrite.Flags,
                       LOWIO_READWRITEFLAG_PAGING_IO)) {
        PSMBCE_V_NET_ROOT_CONTEXT pVNetRootContext;
        PMRX_SRV_CALL             pSrvCall;

        pVNetRootContext = (PSMBCE_V_NET_ROOT_CONTEXT)SrvOpen->pVNetRoot->Context;
        pSrvCall = SrvOpen->pVNetRoot->pNetRoot->pSrvCall;

        RxIndicateChangeOfBufferingStateForSrvOpen(
            pSrvCall,
            SrvOpen,
            MRxSmbMakeSrvOpenKey(pVNetRootContext->TreeId,smbSrvOpen->Fid),
            ULongToPtr(SMB_OPLOCK_LEVEL_NONE));
        SmbCeLog(("Breaking oplock to None in Write SO %lx\n",SrvOpen));
    }

    do {
        Status = __SmbPseCreateOrdinaryExchange(
                               RxContext,
                               capFobx->pSrvOpen->pVNetRoot,
                               SMBPSE_OE_FROM_WRITE,
                               SmbPseExchangeStart_Write,
                               &OrdinaryExchange);

        if (Status != STATUS_SUCCESS) {
            RxDbgTrace(-1, Dbg, ("Couldn't get the smb buf!\n"));

            return Status;
        }

        Status = SmbPseInitiateOrdinaryExchange(OrdinaryExchange);

        if ( Status != STATUS_PENDING ) {
            BOOLEAN FinalizationComplete = SmbPseFinalizeOrdinaryExchange(OrdinaryExchange);
            ASSERT( FinalizationComplete );
        } else {
            ASSERT(BooleanFlagOn(RxContext->Flags,RX_CONTEXT_FLAG_ASYNC_OPERATION));
        }

        if ((Status == STATUS_RETRY) &&
            BooleanFlagOn(RxContext->Flags,RX_CONTEXT_FLAG_ASYNC_OPERATION)) {
            MRxSmbResumeAsyncReadWriteRequests(RxContext);
            Status = STATUS_PENDING;
        }
    } while (Status == STATUS_RETRY);


    RxDbgTrace(-1, Dbg, ("MRxSmbWrite  exit with status=%08lx\n", Status ));

    return(Status);
} // MRxSmbWrite


NTSTATUS
MRxSmbBuildWriteRequest(
    PSMB_PSE_ORDINARY_EXCHANGE OrdinaryExchange,
    BOOLEAN                    IsPagingIo,
    UCHAR                      WriteCommand,
    ULONG                      ByteCount,
    PLARGE_INTEGER             ByteOffsetAsLI,
    PBYTE                      Buffer,
    PMDL                       BufferAsMdl)
/*++

Routine Description:

    This is the start routine for write.

Arguments:

    pExchange - the exchange instance

Return Value:

    NTSTATUS - The return status for the operation

--*/
{
    NTSTATUS Status;

    PSMBSTUFFER_BUFFER_STATE StufferState = &OrdinaryExchange->AssociatedStufferState;
    PRX_CONTEXT RxContext = StufferState->RxContext;

    RxCaptureFcb;
    RxCaptureFobx;

    PLOWIO_CONTEXT LowIoContext = &RxContext->LowIoContext;

    PNT_SMB_HEADER NtSmbHeader = (PNT_SMB_HEADER)(StufferState->BufferBase);

    PMRX_SRV_OPEN SrvOpen = capFobx->pSrvOpen;
    PMRX_SMB_SRV_OPEN smbSrvOpen = MRxSmbGetSrvOpenExtension(SrvOpen);

    ULONG OffsetLow,OffsetHigh;

    PSMB_PSE_OE_READWRITE rw = &OrdinaryExchange->ReadWrite;

    USHORT  WriteMode = 0;
    ULONG   DataLengthLow,DataLengthHigh;
    ULONG   BytesRemaining = 0;
    BOOLEAN AddLengthBytes = FALSE;
    ULONG   WriteCommandSize;

    PSMBCE_SERVER pServer = SmbCeGetExchangeServer((PSMB_EXCHANGE)OrdinaryExchange);
    BOOLEAN UseNtVersion;

    UseNtVersion = BooleanFlagOn(pServer->DialectFlags,DF_NT_SMBS) &&
                   !MRxSmbForceNoNtWriteAndX;

    // The data length field in SMB is a USHORT, and hence the data length given
    // needs to be split up into two parts -- DataLengthHigh and DataLengthLow
    DataLengthLow  = (ByteCount & 0xffff);
    DataLengthHigh = ((ByteCount & 0xffff0000) >> 16);

    OffsetLow  = ByteOffsetAsLI->LowPart;
    OffsetHigh = ByteOffsetAsLI->HighPart;

    switch (WriteCommand) {
    case SMB_COM_WRITE_ANDX:
        WriteCommandSize = SMB_REQUEST_SIZE(NT_WRITE_ANDX);
        break;
    case SMB_COM_WRITE:
        WriteCommandSize = SMB_REQUEST_SIZE(WRITE);
        break;
    case SMB_COM_WRITE_PRINT_FILE:
        WriteCommandSize = SMB_REQUEST_SIZE(WRITE_PRINT_FILE);
        break;
    }

    Status = MRxSmbStartSMBCommand(
                 StufferState,
                 SetInitialSMB_Never,
                 WriteCommand,
                 WriteCommandSize,
                 NO_EXTRA_DATA,
                 NO_SPECIAL_ALIGNMENT,
                 RESPONSE_HEADER_SIZE_NOT_SPECIFIED,
                 0,0,0,0 STUFFERTRACE(Dbg,'FC'));

    MRxSmbDumpStufferState(
        1000,
        "SMB Write Request before stuffing",
        StufferState);

    switch (WriteCommand) {
    case SMB_COM_WRITE_ANDX :
        {
            if ( UseNtVersion && IsPagingIo ) {
                SmbPutAlignedUshort(
                    &NtSmbHeader->Flags2,
                    SmbGetAlignedUshort(&NtSmbHeader->Flags2)|SMB_FLAGS2_PAGING_IO );
            }

            //
            //  If the file object was opened in write through mode, set write
            //  through on the write operation.
            if (FlagOn(RxContext->Flags,RX_CONTEXT_FLAG_WRITE_THROUGH)) {
                WriteMode |= SMB_WMODE_WRITE_THROUGH;
            }

            MRxSmbStuffSMB (
                StufferState,
                "XwddwwwwQ",
                                                  //  X UCHAR WordCount;
                                                  //    UCHAR AndXCommand;
                                                  //    UCHAR AndXReserved;
                                                  //    _USHORT( AndXOffset );
                smbSrvOpen->Fid,                  //  w _USHORT( Fid );
                OffsetLow,                        //  d _ULONG( Offset );
                -1,                               //  d _ULONG( Timeout );
                WriteMode,                        //  w _USHORT( WriteMode );
                BytesRemaining,                   //  w _USHORT( Remaining );
                DataLengthHigh,                   //  w _USHORT( DataLengthHigh );
                DataLengthLow,                    //  w _USHORT( DataLength );
                                                  //  Q _USHORT( DataOffset );
                SMB_OFFSET_CHECK(WRITE_ANDX,DataOffset)
                StufferCondition(UseNtVersion), "D",
                SMB_OFFSET_CHECK(NT_WRITE_ANDX,OffsetHigh)
                OffsetHigh,                       //  D NTonly  _ULONG( OffsetHigh );
                                                  //
                STUFFER_CTL_NORMAL, "BS5",
                                                  //  B _USHORT( ByteCount );
                SMB_WCT_CHECK(((UseNtVersion)?14:12))
                                                  //    UCHAR Buffer[1];
                                                  //  S //UCHAR Pad[];
                                                  //  5 //UCHAR Data[];
                StufferCondition(AddLengthBytes), "w", LowIoContext->ParamsFor.ReadWrite.ByteCount,
                StufferCondition(Buffer!=NULL), "c!",
                ByteCount,
                Buffer,                           //  c the actual data
                0
                );
        }
        break;

    case SMB_COM_WRITE :
        {
            MRxSmbStuffSMB (
                StufferState,
                "0wwdwByw",
                                       //  0   UCHAR WordCount;                    // Count of parameter words = 5
                smbSrvOpen->Fid,       //  w   _USHORT( Fid );                     // File handle
                DataLengthLow,         //  w   _USHORT( Count );                   // Number of bytes to be written
                OffsetLow,             //  d   _ULONG( Offset );                   // Offset in file to begin write
                BytesRemaining,        //  w   _USHORT( Remaining );               // Bytes remaining to satisfy request
                SMB_WCT_CHECK(5)       //  B   _USHORT( ByteCount );               // Count of data bytes
                                            //      //UCHAR Buffer[1];                  // Buffer containing:
                0x01,                  //  y     UCHAR BufferFormat;               //  0x01 -- Data block
                DataLengthLow,            //  w     _USHORT( DataLength );            //  Length of data
                                       //        ULONG Buffer[1];                  //  Data
                StufferCondition(Buffer!=NULL), "c!",
                ByteCount,
                Buffer,     //  c     the actual data
                0
                );
        }
        break;

    case SMB_COM_WRITE_PRINT_FILE:
        {
            MRxSmbStuffSMB (
                StufferState,
                "0wByw",
                                       // 0  UCHAR WordCount;                    // Count of parameter words = 1
                smbSrvOpen->Fid,       // w  _USHORT( Fid );                     // File handle
                SMB_WCT_CHECK(1)       // B  _USHORT( ByteCount );               // Count of data bytes; min = 4
                                            //    UCHAR Buffer[1];                    // Buffer containing:
                0x01,                  // y  //UCHAR BufferFormat;               //  0x01 -- Data block
                DataLengthLow,         // w  //USHORT DataLength;                //  Length of data
                                            //    //UCHAR Data[];                     //  Data
                StufferCondition(Buffer!=NULL), "c!",
                ByteCount,
                Buffer,     //  c     the actual data
                0
                );
        }
        break;

    default:
        Status = STATUS_UNSUCCESSFUL ;
        break;
    }

    if ( BufferAsMdl ) {
        MRxSmbStuffAppendRawData( StufferState, BufferAsMdl );
        MRxSmbStuffSetByteCount( StufferState );
    }

    MRxSmbDumpStufferState(
        700,
        "SMB Write Request after stuffing",
        StufferState);

    if (Status==STATUS_SUCCESS) {
        InterlockedIncrement(&MRxSmbStatistics.SmallWriteSmbs);
    }

    return Status;
}

BOOLEAN DisableLargeWrites = 0;

NTSTATUS
SmbPseExchangeStart_Write (
    SMBPSE_ORDINARY_EXCHANGE_ARGUMENT_SIGNATURE
    )
/*++

Routine Description:

    This is the start routine for write.

Arguments:

    pExchange - the exchange instance

Return Value:

    NTSTATUS - The return status for the operation

--*/
{
    NTSTATUS Status;

    ULONG StartEntryCount;

    PSMBSTUFFER_BUFFER_STATE StufferState = &OrdinaryExchange->AssociatedStufferState;
    PSMB_PSE_OE_READWRITE rw = &OrdinaryExchange->ReadWrite;

    PLOWIO_CONTEXT LowIoContext = &RxContext->LowIoContext;
    PMDL OriginalDataMdl = LowIoContext->ParamsFor.ReadWrite.Buffer;

    RxCaptureFcb;
    RxCaptureFobx;

    PMRX_SRV_OPEN SrvOpen = capFobx->pSrvOpen;
    PMRX_SMB_SRV_OPEN smbSrvOpen = MRxSmbGetSrvOpenExtension(SrvOpen);
    PMRX_SMB_FCB  SmbFcb = MRxSmbGetFcbExtension(capFcb);

    BOOLEAN SynchronousIo, IsPagingIo;
    BOOLEAN WriteToTheEnd = FALSE;
    UCHAR   WriteCommand;

    PAGED_CODE();
    RxDbgTrace(+1, Dbg, ("SmbPseExchangeStart_Write\n"));

    ASSERT( OrdinaryExchange->Type == ORDINARY_EXCHANGE );

    ASSERT(
        (
            (OriginalDataMdl!=NULL) &&
            (
                RxMdlIsLocked(OriginalDataMdl) ||
                RxMdlSourceIsNonPaged(OriginalDataMdl)
            )
        ) ||
        (
            (OriginalDataMdl==NULL) &&
            (LowIoContext->ParamsFor.ReadWrite.ByteCount==0)
        )
        );

    ASSERT((OrdinaryExchange->SmbCeFlags&SMBCE_EXCHANGE_ATTEMPT_RECONNECTS) == 0 );

    OrdinaryExchange->StartEntryCount++;
    StartEntryCount = OrdinaryExchange->StartEntryCount;

    SynchronousIo = !BooleanFlagOn(
                        RxContext->Flags,
                        RX_CONTEXT_FLAG_ASYNC_OPERATION);

    IsPagingIo = BooleanFlagOn(
                     LowIoContext->ParamsFor.ReadWrite.Flags,
                     LOWIO_READWRITEFLAG_PAGING_IO);

    // Ensure that the Fid is validated
    SetFlag(OrdinaryExchange->Flags,SMBPSE_OE_FLAG_VALIDATE_FID);

    for (;;) {
        PSMBCE_SERVER         pServer;
        PSMBCE_NET_ROOT       pNetRoot;

        pServer  = SmbCeGetExchangeServer(OrdinaryExchange);
        pNetRoot = SmbCeGetExchangeNetRoot(OrdinaryExchange);

        switch (OrdinaryExchange->OpSpecificState) {
        case SmbPseOEInnerIoStates_Initial:
            {
                OrdinaryExchange->OpSpecificState = SmbPseOEInnerIoStates_ReadyToSend;

                if ( !SynchronousIo ) {
                    OrdinaryExchange->AsyncResumptionRoutine = SmbPseExchangeStart_Write;
                }

                MRxSmbSetInitialSMB( StufferState STUFFERTRACE(Dbg,'FC') );

                rw->UserBufferBase = RxLowIoGetBufferAddress( RxContext );
                rw->ByteOffsetAsLI.QuadPart = LowIoContext->ParamsFor.ReadWrite.ByteOffset;
                rw->RemainingByteCount = LowIoContext->ParamsFor.ReadWrite.ByteCount;

                if (rw->ByteOffsetAsLI.QuadPart == -1 ) {
                    WriteToTheEnd = TRUE;
                    rw->ByteOffsetAsLI.QuadPart = smbSrvOpen->FileInfo.Standard.EndOfFile.QuadPart;
                }

                if (OriginalDataMdl != NULL) {
                    rw->UserBufferBase = RxLowIoGetBufferAddress( RxContext );
                } else {
                    rw->UserBufferBase = (PBYTE)1;   //any nonzero value will do
                }

                rw->ThisBufferOffset = 0;

                rw->PartialExchangeMdlInUse = FALSE;
                rw->PartialDataMdlInUse     = FALSE;
            }
            //lack of break is intentional

        case SmbPseOEInnerIoStates_ReadyToSend:
            {
                ULONG MaximumBufferSizeThisIteration;
                PCHAR Buffer = NULL;
                PMDL  BufferAsMdl = NULL;

                OrdinaryExchange->OpSpecificState = SmbPseOEInnerIoStates_OperationOutstanding;
                OrdinaryExchange->SendOptions = MRxSmbWriteSendOptions;

                if (FlagOn(pServer->DialectFlags,DF_LANMAN10) &&
                    FlagOn(pServer->DialectFlags,DF_LARGE_FILES) &&
                    (StufferState->RxContext->pFcb->pNetRoot->Type != NET_ROOT_PRINT)) {
                    WriteCommand = SMB_COM_WRITE_ANDX;
                } else if (StufferState->RxContext->pFcb->pNetRoot->Type == NET_ROOT_PRINT){
                    WriteCommand = SMB_COM_WRITE_PRINT_FILE;
                } else {
                    WriteCommand = SMB_COM_WRITE;
                }

                MaximumBufferSizeThisIteration = pNetRoot->MaximumWriteBufferSize;

                // There are four parameters pertaining to a write request
                //
                //  1. Write Length -- rw->ThisByteCount
                //  2. Write Offset -- rw->ByteOffsetAsLI
                //  3. Write Buffer -- Buffer
                //  4. Write Buffer as a MDL -- BufferAsMdl
                //
                // All writes can be classified into one of the following
                // categories ...
                //
                //  1. Extremely Small writes
                //      These are writes lesser than the COPY_THRESHOLD or
                //      we are in a debug mode that forces us to do only small
                //      writes.
                //
                //  2. Write requests against downlevel servers or non disk
                //     file write requests against up level servers.
                //      In all these cases we are constrained by the Server
                //      which limits the number of bytes to roughly 4k. This
                //      is based upon the Smb Buffer size returned during
                //      negotiation.
                //
                //  3. Write requests against uplevel (NT5+)
                //     servers
                //      These write requests can be arbitrarily large
                //


                if ((rw->RemainingByteCount < WRITE_COPY_THRESHOLD) ||
                    FORCECOPYMODE) {
                    if (FORCECOPYMODE &&
                        (rw->ThisByteCount > MaximumBufferSizeThisIteration) ) {
                        rw->ThisByteCount = MaximumBufferSizeThisIteration;
                    } else {
                        rw->ThisByteCount = rw->RemainingByteCount;
                    }

                    Buffer = rw->UserBufferBase + rw->ThisBufferOffset;

                    ASSERT( WRITE_COPY_THRESHOLD <= pNetRoot->MaximumWriteBufferSize );
                } else {
                    rw->ThisByteCount = min(
                                            rw->RemainingByteCount,
                                            MaximumBufferSizeThisIteration);

                    if ((rw->ThisBufferOffset != 0) ||
                        (rw->ThisByteCount != OriginalDataMdl->ByteCount)) {
                        MmInitializeMdl(
                            &rw->PartialDataMdl,
                            0,
                            MAX_PARTIAL_DATA_MDL_BUFFER_SIZE);

                        IoBuildPartialMdl(
                            OriginalDataMdl,
                            &rw->PartialDataMdl,
                            (PCHAR)MmGetMdlVirtualAddress(OriginalDataMdl) +
                                rw->ThisBufferOffset,
                            rw->ThisByteCount );

                        BufferAsMdl = &rw->PartialDataMdl;
                    } else {
                        BufferAsMdl = OriginalDataMdl;
                    }
                }

                Status = MRxSmbBuildWriteRequest(
                             OrdinaryExchange,
                             IsPagingIo,
                             WriteCommand,
                             rw->ThisByteCount,
                             &rw->ByteOffsetAsLI,
                             Buffer,
                             BufferAsMdl);

                if (Status != STATUS_SUCCESS) {
                    RxDbgTrace(0, Dbg, ("bad write stuffer status........\n"));
                    goto FINALLY;
                }

                InterlockedIncrement(&MRxSmbStatistics.WriteSmbs);

                Status = SmbPseOrdinaryExchange(
                             SMBPSE_ORDINARY_EXCHANGE_ARGUMENTS,
                             SMBPSE_OETYPE_WRITE );

                if ( Status == STATUS_PENDING) {
                    ASSERT( !SynchronousIo );
                    goto FINALLY;
                }
            }
            //lack of break is intentional

        case SmbPseOEInnerIoStates_OperationOutstanding:
        case SmbPseOEInnerIoStates_OperationCompleted:
            {
                SetFlag(OrdinaryExchange->OpSpecificFlags,OE_RW_FLAG_SUBSEQUENT_OPERATION);

                OrdinaryExchange->OpSpecificState = SmbPseOEInnerIoStates_ReadyToSend;

                if (rw->PartialExchangeMdlInUse) {
                    MmPrepareMdlForReuse(
                        &rw->PartialExchangeMdl);
                    rw->PartialDataMdlInUse = FALSE;
                }

                if (rw->PartialDataMdlInUse) {
                    MmPrepareMdlForReuse(
                        &rw->PartialDataMdl);
                    rw->PartialDataMdlInUse = FALSE;
                }

                Status = OrdinaryExchange->Status;

                if (Status == STATUS_SMB_USE_STANDARD) {
                    // Send the remaining data using Restart all over again and
                    rw->UserBufferBase = RxLowIoGetBufferAddress( RxContext );
                    rw->ByteOffsetAsLI.QuadPart = LowIoContext->ParamsFor.ReadWrite.ByteOffset;
                    rw->RemainingByteCount = LowIoContext->ParamsFor.ReadWrite.ByteCount;

                    if (rw->ByteOffsetAsLI.QuadPart == -1 ) {
                        WriteToTheEnd = TRUE;
                        rw->ByteOffsetAsLI.QuadPart = smbSrvOpen->FileInfo.Standard.EndOfFile.QuadPart;
                    }

                    rw->BytesReturned = 0;
                    rw->ThisByteCount = 0;
                    rw->ThisBufferOffset = 0;

                    RxContext->InformationToReturn = 0;

                    OrdinaryExchange->Status = STATUS_SUCCESS;
                    Status = STATUS_SUCCESS;
                }

                rw->RemainingByteCount -= rw->BytesReturned;
                RxContext->InformationToReturn += rw->BytesReturned;

                if (Status == STATUS_SUCCESS) {
                    rw->ByteOffsetAsLI.QuadPart += rw->BytesReturned;
                    rw->ThisBufferOffset += rw->BytesReturned;

                    if (WriteToTheEnd) {
                        smbSrvOpen->FileInfo.Standard.EndOfFile.QuadPart += rw->BytesReturned;
                    }
                }

                if ((Status != STATUS_SUCCESS) ||
                    (rw->RemainingByteCount == 0)) {
                    PSMBCE_SESSION pSession = SmbCeGetExchangeSession(OrdinaryExchange);

                    RxDbgTrace(
                        0,
                        Dbg,
                        (
                         "OE %lx TBC %lx RBC %lx BR %lx TBO %lx\n",
                         OrdinaryExchange,rw->ThisByteCount,
                         rw->RemainingByteCount,
                         rw->BytesReturned,
                         rw->ThisBufferOffset )
                        );

                    RxDbgTrace(
                        0,
                        Dbg,
                        ("Bytes written %lx\n",
                         RxContext->InformationToReturn)
                        );

                    goto FINALLY;
                }

                RxDbgTrace(
                    0,
                    Dbg,
                    ( "Next Iteration OE %lx RBC %lx TBO %lx\n",
                      OrdinaryExchange,
                      rw->RemainingByteCount,
                      rw->ThisBufferOffset)
                    );

                RxDbgTrace(
                    0,
                    Dbg,
                    ("OE %lx TBC %lx, BR %lx\n",
                     OrdinaryExchange,
                     rw->ThisByteCount,
                     rw->BytesReturned));

                MRxSmbSetInitialSMB(StufferState STUFFERTRACE(Dbg,0));
            }
            break;
        }
    }


FINALLY:
    if ( Status != STATUS_PENDING) {
        if (Status != STATUS_RETRY) {
            SmbPseAsyncCompletionIfNecessary(OrdinaryExchange,RxContext);
        }
    }

    RxDbgTrace(-1, Dbg, ("SmbPseExchangeStart_Write exit w %08lx\n", Status ));
    return Status;

} // SmbPseExchangeStart_Write


NTSTATUS
MRxSmbFinishWrite (
    IN OUT  PSMB_PSE_ORDINARY_EXCHANGE  OrdinaryExchange,
    IN      PBYTE                       ResponseBuffer
    )
/*++

Routine Description:

    This routine actually gets the stuff out of the write response and finishes
    the write. Everything you need is locked down... so we can finish in the
    indication routine

Arguments:

    OrdinaryExchange - the exchange instance

    ResponseBuffer - the response

Return Value:

    RXSTATUS - The return status for the operation

--*/
{
    NTSTATUS Status = STATUS_SUCCESS;
    ULONG BytesReturned;

    PAGED_CODE();

    RxDbgTrace(+1, Dbg, ("MRxSmbFinishWrite\n"));
    SmbPseOEAssertConsistentLinkageFromOE("MRxSmbFinishWrite:");

    switch (OrdinaryExchange->LastSmbCommand) {
    case SMB_COM_WRITE_ANDX:
        {
            PSMBCE_SERVER    pServer;
            PSMBCE_NET_ROOT  pNetRoot;
            PRESP_WRITE_ANDX Response = (PRESP_WRITE_ANDX)ResponseBuffer;

            if (Response->WordCount != 6 ||
                SmbGetUshort(&Response->ByteCount) != 0) {
                Status = STATUS_INVALID_NETWORK_RESPONSE;
            }

            pServer = SmbCeGetExchangeServer((PSMB_EXCHANGE)OrdinaryExchange);
            pNetRoot = SmbCeGetExchangeNetRoot((PSMB_EXCHANGE)OrdinaryExchange);

            BytesReturned = SmbGetUshort( &Response->Count );

            if (FlagOn(pServer->DialectFlags,DF_LARGE_WRITEX)) {
                ULONG BytesReturnedHigh;

                BytesReturnedHigh = SmbGetUshort(&Response->CountHigh);

                BytesReturned = (BytesReturnedHigh << 16) | BytesReturned;
            }

            if ((OrdinaryExchange->Status == STATUS_SUCCESS) &&
                (OrdinaryExchange->ReadWrite.ThisByteCount > 2) &&
                (BytesReturned == 0)) {
                    Status = STATUS_INVALID_NETWORK_RESPONSE;
            }

            //if we added 2 headerbytes then let's get rid of them......
            if ( FlagOn(OrdinaryExchange->OpSpecificFlags,OE_RW_FLAG_REDUCE_RETURNCOUNT) ) {
                // BytesReturned -= sizeof(USHORT);
                ClearFlag(OrdinaryExchange->OpSpecificFlags,OE_RW_FLAG_REDUCE_RETURNCOUNT);
            }
        }
        break;

    case SMB_COM_WRITE :
        {
            PRESP_WRITE  Response = (PRESP_WRITE)ResponseBuffer;

            if (Response->WordCount != 1 ||
                SmbGetUshort(&Response->ByteCount) != 0) {
                Status = STATUS_INVALID_NETWORK_RESPONSE;
            }

            BytesReturned = SmbGetUshort( &Response->Count );
        }
        break;

    case SMB_COM_WRITE_PRINT_FILE:
        {
            PRESP_WRITE_PRINT_FILE Response = (PRESP_WRITE_PRINT_FILE)ResponseBuffer;

            if (Response->WordCount != 0) {
                Status = STATUS_INVALID_NETWORK_RESPONSE;
            }

            //the response does not tell how many bytes were taken! get the byte count from the exchange
            BytesReturned = OrdinaryExchange->ReadWrite.ThisByteCount;
        }
        break;

    default :
        Status = STATUS_INVALID_NETWORK_RESPONSE;
        break;
    }

    RxDbgTrace(0, Dbg, ("-->BytesReturned=%08lx\n", BytesReturned));

    OrdinaryExchange->ReadWrite.BytesReturned = BytesReturned;

    if (Status == STATUS_SUCCESS &&
        OrdinaryExchange->ReadWrite.ThisByteCount > 2 &&
        BytesReturned > OrdinaryExchange->ReadWrite.ThisByteCount) {
        Status = STATUS_INVALID_NETWORK_RESPONSE;
    }

    RxDbgTrace(-1, Dbg, ("MRxSmbFinishWrite   returning %08lx\n", Status ));

    return Status;
} // MRxSmbFinishWrite



