/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*-
 * ex: set softtabstop=4 tabstop=8 expandtab shiftwidth=4: *
 * Editor Settings: expandtabs and use 4 spaces for indentation */

/*
 * Copyright Likewise Software
 * All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.  You should have received a copy of the GNU General
 * Public License along with this program.  If not, see
 * <http://www.gnu.org/licenses/>.
 *
 * LIKEWISE SOFTWARE MAKES THIS SOFTWARE AVAILABLE UNDER OTHER LICENSING
 * TERMS AS WELL.  IF YOU HAVE ENTERED INTO A SEPARATE LICENSE AGREEMENT
 * WITH LIKEWISE SOFTWARE, THEN YOU MAY ELECT TO USE THE SOFTWARE UNDER THE
 * TERMS OF THAT SOFTWARE LICENSE AGREEMENT INSTEAD OF THE TERMS OF THE GNU
 * GENERAL PUBLIC LICENSE, NOTWITHSTANDING THE ABOVE NOTICE.  IF YOU
 * HAVE QUESTIONS, OR WISH TO REQUEST A COPY OF THE ALTERNATE LICENSING
 * TERMS OFFERED BY LIKEWISE SOFTWARE, PLEASE CONTACT LIKEWISE SOFTWARE AT
 * license@likewisesoftware.com
 */

#include "includes.h"

static
NTSTATUS
SrvQuerySecurityDescriptor(
    PSRV_EXEC_CONTEXT pExecContext
    );

static
NTSTATUS
SrvExecuteQuerySecurityDescriptor(
    PSRV_EXEC_CONTEXT pExecContext
    );

static
NTSTATUS
SrvBuildQuerySecurityDescriptorResponse(
    PSRV_EXEC_CONTEXT pExecContext
    );

static
NTSTATUS
SrvSetSecurityDescriptor(
    PSRV_EXEC_CONTEXT pExecContext
    );

static
NTSTATUS
SrvBuildSetSecurityDescriptorResponse(
    PSRV_EXEC_CONTEXT pExecContext
    );

static
NTSTATUS
SrvProcessIOCTL(
    PSRV_EXEC_CONTEXT pExecContext
    );

static
NTSTATUS
SrvExecuteIoctl(
    PSRV_EXEC_CONTEXT pExecContext
    );

static
NTSTATUS
SrvExecuteEnumerateSnapshots(
    PSRV_EXEC_CONTEXT pExecContext
    );

static
NTSTATUS
SrvBuildIOCTLResponse(
    PSRV_EXEC_CONTEXT pExecContext
    );

static
NTSTATUS
SrvExecuteQueryQuota(
    PSRV_EXEC_CONTEXT pExecContext
    );

static
NTSTATUS
SrvBuildQueryQuotaResponse(
    PSRV_EXEC_CONTEXT pExecContext
    );

static
NTSTATUS
SrvExecuteSetQuota(
    PSRV_EXEC_CONTEXT pExecContext
    );

static
NTSTATUS
SrvBuildSetQuotaResponse(
    PSRV_EXEC_CONTEXT pExecContext
    );

static
NTSTATUS
SrvProcessNotifyChange(
    PSRV_EXEC_CONTEXT pExecContext
    );

static
NTSTATUS
SrvExecuteChangeNotify(
    PSRV_EXEC_CONTEXT               pExecContext,
    PSRV_CHANGE_NOTIFY_STATE_SMB_V1 pNotifyState
    );

static
NTSTATUS
SrvProcessChangeNotifyCompletion(
    PSRV_EXEC_CONTEXT pExecContext
    );

static
NTSTATUS
SrvBuildChangeNotifyResponse(
    PSRV_EXEC_CONTEXT               pExecContext,
    PSRV_CHANGE_NOTIFY_STATE_SMB_V1 pNotifyState
    );

static
NTSTATUS
SrvProcessNtTransactCreate(
    PSRV_EXEC_CONTEXT pExecContext
    );

static
NTSTATUS
SrvProcessNtTransactQueryQuota(
    PSRV_EXEC_CONTEXT pExecContext
    );

static
NTSTATUS
SrvProcessNtTransactSetQuota(
    PSRV_EXEC_CONTEXT pExecContext
    );

static
NTSTATUS
SrvParseNtTransactCreateParameters(
    PSRV_EXEC_CONTEXT pExecContext
    );

static
VOID
SrvLogNtTransactCreateState_SMB_V1(
    PSRV_LOG_CONTEXT pLogContext,
    LWIO_LOG_LEVEL   logLevel,
    PCSTR            pszFunction,
    PCSTR            pszFile,
    ULONG            ulLine,
    ...
    );

static
NTSTATUS
SrvQueryNTTransactFileInformation(
    PSRV_EXEC_CONTEXT pExecContext
    );

static
NTSTATUS
SrvRequestNTTransactOplocks(
    PSRV_EXEC_CONTEXT pExecContext
    );

static
NTSTATUS
SrvBuildNTTransactState(
    PNT_TRANSACTION_REQUEST_HEADER pRequestHeader,
    PUSHORT                        pusBytecount,
    PUSHORT                        pSetup,
    PBYTE                          pParameters,
    PBYTE                          pData,
    PSRV_NTTRANSACT_STATE_SMB_V1*  ppNTTransactState
    );

static
NTSTATUS
SrvBuildNTTransactCreateResponse(
    PSRV_EXEC_CONTEXT pExecContext
    );

static
VOID
SrvFreeNTTransactStateHandle(
    HANDLE hNTTransactState
    );

static
VOID
SrvFreeNTTransactState(
    PSRV_NTTRANSACT_STATE_SMB_V1 pNTTransactState
    );

NTSTATUS
SrvProcessNtTransact(
    PSRV_EXEC_CONTEXT pExecContext
    )
{
    NTSTATUS                   ntStatus     = 0;
    PLWIO_SRV_CONNECTION       pConnection  = pExecContext->pConnection;
    PSRV_EXEC_CONTEXT_SMB_V1   pCtxSmb1     = pExecContext->pProtocolContext;
    ULONG                      iMsg         = pCtxSmb1->iMsg;
    PSRV_MESSAGE_SMB_V1        pSmbRequest  = &pCtxSmb1->pRequests[iMsg];
    PSRV_NTTRANSACT_STATE_SMB_V1 pNTTransactState = pCtxSmb1->hState;

    if (pNTTransactState)
    {
        ntStatus = SRV_SCHEDULE_IF(
                        pExecContext,
                        ((pNTTransactState->pRequestHeader->usFunction == SMB_SUB_COMMAND_NT_TRANSACT_CREATE) &&
                         (pNTTransactState->hFile || pNTTransactState->pFile)));
        BAIL_ON_NT_STATUS(ntStatus);
    }
    else
    {
        PBYTE pBuffer          = pSmbRequest->pBuffer + pSmbRequest->usHeaderSize;
        ULONG ulOffset         = pSmbRequest->usHeaderSize;
        ULONG ulBytesAvailable = pSmbRequest->ulMessageSize - pSmbRequest->usHeaderSize;
        PNT_TRANSACTION_REQUEST_HEADER pRequestHeader = NULL; // Do not free
        PUSHORT                        pusBytecount   = NULL; // Do not free
        PUSHORT                        pSetup         = NULL; // Do not free
        PBYTE                          pParameters    = NULL; // Do not free
        PBYTE                          pData          = NULL; // Do not free

        ntStatus = WireUnmarshallNtTransactionRequest(
                        pBuffer,
                        ulBytesAvailable,
                        ulOffset,
                        &pRequestHeader,
                        &pSetup,
                        &pusBytecount,
                        &pParameters,
                        &pData);
        BAIL_ON_NT_STATUS(ntStatus);

        SRV_LOG_DEBUG(
                pExecContext->pLogContext,
                SMB_PROTOCOL_VERSION_1,
                pSmbRequest->pHeader->command,
                "NT Transact Params: command(%u),uid(%u),mid(%u),pid(%u),"
                "tid(%u),function(%u)",
                pSmbRequest->pHeader->command,
                pSmbRequest->pHeader->uid,
                pSmbRequest->pHeader->mid,
                SMB_V1_GET_PROCESS_ID(pSmbRequest->pHeader),
                pSmbRequest->pHeader->tid,
                pRequestHeader->usFunction);

        ntStatus = SrvBuildNTTransactState(
                        pRequestHeader,
                        pusBytecount,
                        pSetup,
                        pParameters,
                        pData,
                        &pNTTransactState);
        BAIL_ON_NT_STATUS(ntStatus);

        pCtxSmb1->hState = pNTTransactState;
        pCtxSmb1->pfnStateRelease = &SrvFreeNTTransactStateHandle;

        ntStatus = SrvConnectionFindSession_SMB_V1(
                        pCtxSmb1,
                        pConnection,
                        pSmbRequest->pHeader->uid,
                        &pNTTransactState->pSession);
        BAIL_ON_NT_STATUS(ntStatus);

        ntStatus = SrvSetStatSessionInfo(
                        pExecContext,
                        pNTTransactState->pSession);
        BAIL_ON_NT_STATUS(ntStatus);

        ntStatus = SrvSessionFindTree_SMB_V1(
                        pCtxSmb1,
                        pNTTransactState->pSession,
                        pSmbRequest->pHeader->tid,
                        &pNTTransactState->pTree);
        BAIL_ON_NT_STATUS(ntStatus);
    }

    if (pExecContext->pStatInfo)
    {
        ntStatus = SrvStatisticsSetSubOpcode(
                        pExecContext->pStatInfo,
                        pNTTransactState->pRequestHeader->usFunction);
        BAIL_ON_NT_STATUS(ntStatus);
    }

    switch (pNTTransactState->pRequestHeader->usFunction)
    {
        case SMB_SUB_COMMAND_NT_TRANSACT_QUERY_SECURITY_DESC:

            ntStatus = SrvQuerySecurityDescriptor(pExecContext);

            break;

        case SMB_SUB_COMMAND_NT_TRANSACT_SET_SECURITY_DESC :

            ntStatus = SrvSetSecurityDescriptor(pExecContext);

            break;

        case SMB_SUB_COMMAND_NT_TRANSACT_IOCTL :

            ntStatus = SrvProcessIOCTL(pExecContext);

            break;

        case SMB_SUB_COMMAND_NT_TRANSACT_NOTIFY_CHANGE :

            ntStatus = SrvProcessNotifyChange(pExecContext);

            break;

        case SMB_SUB_COMMAND_NT_TRANSACT_CREATE :

            ntStatus = SrvProcessNtTransactCreate(pExecContext);

            break;

        case SMB_SUB_COMMAND_NT_TRANSACT_QUERY_QUOTA :

            ntStatus = SrvProcessNtTransactQueryQuota(pExecContext);

            break;

        case SMB_SUB_COMMAND_NT_TRANSACT_SET_QUOTA :

            ntStatus = SrvProcessNtTransactSetQuota(pExecContext);

            break;

        case SMB_SUB_COMMAND_NT_TRANSACT_RENAME :

            ntStatus = STATUS_NOT_SUPPORTED;

            break;

        default:

            ntStatus = STATUS_INVALID_NETWORK_RESPONSE;
            break;
    }
    BAIL_ON_NT_STATUS(ntStatus);

cleanup:

    return ntStatus;

error:

    switch (ntStatus)
    {
        case STATUS_PENDING:

            // TODO: Add an indicator to the file object to trigger a
            //       cleanup if the connection gets closed and all the
            //       files involved have to be closed

            break;

        default:

            SrvReleaseExecContextAsync(pExecContext);

            break;
    }

    goto cleanup;
}

NTSTATUS
SrvProcessNtTransactInternal(
    PSRV_EXEC_CONTEXT pExecContext
    )
{
    NTSTATUS                   ntStatus     = 0;
    PSRV_EXEC_CONTEXT_SMB_V1   pCtxSmb1     = pExecContext->pProtocolContext;
    ULONG                      iMsg         = pCtxSmb1->iMsg;
    PSRV_MESSAGE_SMB_V1        pSmbRequest  = &pCtxSmb1->pRequests[iMsg];
    PBYTE pBuffer          = pSmbRequest->pBuffer + pSmbRequest->usHeaderSize;
    ULONG ulOffset         = pSmbRequest->usHeaderSize;
    ULONG ulBytesAvailable = pSmbRequest->ulMessageSize - pSmbRequest->usHeaderSize;
    PNT_TRANSACTION_REQUEST_HEADER pRequestHeader = NULL; // Do not free
    PUSHORT                        pusBytecount   = NULL; // Do not free
    PUSHORT                        pSetup         = NULL; // Do not free
    PBYTE                          pParameters    = NULL; // Do not free
    PBYTE                          pData          = NULL; // Do not free

    ntStatus = WireUnmarshallNtTransactionRequest(
                    pBuffer,
                    ulBytesAvailable,
                    ulOffset,
                    &pRequestHeader,
                    &pSetup,
                    &pusBytecount,
                    &pParameters,
                    &pData);
    BAIL_ON_NT_STATUS(ntStatus);

    switch (pRequestHeader->usFunction)
    {
        case SMB_SUB_COMMAND_NT_TRANSACT_NOTIFY_CHANGE :

            ntStatus = SrvProcessChangeNotifyCompletion(pExecContext);
            BAIL_ON_NT_STATUS(ntStatus);

            break;

        default:

            ntStatus = STATUS_INVALID_NETWORK_RESPONSE;
            BAIL_ON_NT_STATUS(ntStatus);

            break;
    }

error:

    return ntStatus;
}

static
NTSTATUS
SrvQuerySecurityDescriptor(
    PSRV_EXEC_CONTEXT pExecContext
    )
{
    NTSTATUS ntStatus = 0;
    PSRV_EXEC_CONTEXT_SMB_V1   pCtxSmb1     = pExecContext->pProtocolContext;
    PSRV_NTTRANSACT_STATE_SMB_V1 pNTTransactState = pCtxSmb1->hState;

    switch (pNTTransactState->stage)
    {
        case SRV_NTTRANSACT_STAGE_SMB_V1_INITIAL:

            if (pNTTransactState->pRequestHeader->ulTotalParameterCount !=
                                sizeof(SMB_SECURITY_INFORMATION_HEADER))
            {
                ntStatus = STATUS_INVALID_NETWORK_RESPONSE;
                BAIL_ON_NT_STATUS(ntStatus);
            }

            pNTTransactState->pSecurityRequestHeader =
                (PSMB_SECURITY_INFORMATION_HEADER)pNTTransactState->pParameters;

            SRV_LOG_DEBUG(
                    pExecContext->pLogContext,
                    SMB_PROTOCOL_VERSION_1,
                    pCtxSmb1->pRequests[pCtxSmb1->iMsg].pHeader->command,
                    "NT Transact (Query Sec Desc): "
                    "command(%u),uid(%u),mid(%u),pid(%u),tid(%u),"
                    "file-id(%u),sec-info(%u)",
                    pCtxSmb1->pRequests[pCtxSmb1->iMsg].pHeader->command,
                    pCtxSmb1->pRequests[pCtxSmb1->iMsg].pHeader->uid,
                    pCtxSmb1->pRequests[pCtxSmb1->iMsg].pHeader->mid,
                    SMB_V1_GET_PROCESS_ID(pCtxSmb1->pRequests[pCtxSmb1->iMsg].pHeader),
                    pCtxSmb1->pRequests[pCtxSmb1->iMsg].pHeader->tid,
                    pNTTransactState->pSecurityRequestHeader->usFid,
                    pNTTransactState->pSecurityRequestHeader->ulSecurityInfo);

            ntStatus = SrvTreeFindFile_SMB_V1(
                            pCtxSmb1,
                            pNTTransactState->pTree,
                            pNTTransactState->pSecurityRequestHeader->usFid,
                            &pNTTransactState->pFile);
            BAIL_ON_NT_STATUS(ntStatus);

            pNTTransactState->stage = SRV_NTTRANSACT_STAGE_SMB_V1_ATTEMPT_IO;

            // intentional fall through

        case SRV_NTTRANSACT_STAGE_SMB_V1_ATTEMPT_IO:

            ntStatus = SrvExecuteQuerySecurityDescriptor(pExecContext);
            BAIL_ON_NT_STATUS(ntStatus);

            pNTTransactState->stage = SRV_NTTRANSACT_STAGE_SMB_V1_BUILD_RESPONSE;

            // intentional fall through

        case SRV_NTTRANSACT_STAGE_SMB_V1_BUILD_RESPONSE:

            ntStatus = SrvBuildQuerySecurityDescriptorResponse(pExecContext);
            BAIL_ON_NT_STATUS(ntStatus);

            pNTTransactState->stage = SRV_NTTRANSACT_STAGE_SMB_V1_DONE;

            // intentional fall through

        case SRV_NTTRANSACT_STAGE_SMB_V1_DONE:

            break;

        case SRV_NTTRANSACT_STAGE_SMB_V1_CREATE_COMPLETED:
        case SRV_NTTRANSACT_STAGE_SMB_V1_QUERY_INFO:
        case SRV_NTTRANSACT_STAGE_SMB_V1_GET_OPLOCKS:

            ntStatus = STATUS_INTERNAL_ERROR;

            break;
    }

error:

    return ntStatus;
}

static
NTSTATUS
SrvExecuteQuerySecurityDescriptor(
    PSRV_EXEC_CONTEXT pExecContext
    )
{
    NTSTATUS                   ntStatus     = STATUS_SUCCESS;
    PSRV_EXEC_CONTEXT_SMB_V1   pCtxSmb1     = pExecContext->pProtocolContext;
    BOOLEAN                    bContinue    = TRUE;
    PSRV_NTTRANSACT_STATE_SMB_V1 pNTTransactState = pCtxSmb1->hState;

    do
    {
        ntStatus = pExecContext->ioStatusBlock.Status;

        switch (ntStatus)
        {
            case STATUS_BUFFER_TOO_SMALL:

                if (!pNTTransactState->pSecurityDescriptor2)
                {
                    ULONG ulSecurityDescInitialLen = 256;

                    ntStatus = SrvAllocateMemory(
                                    ulSecurityDescInitialLen,
                                    OUT_PPVOID(&pNTTransactState->pSecurityDescriptor2));
                    BAIL_ON_NT_STATUS(ntStatus);

                    pNTTransactState->ulSecurityDescAllocLen =
                                                    ulSecurityDescInitialLen;
                }
                else if (pNTTransactState->ulSecurityDescAllocLen !=
                                    SECURITY_DESCRIPTOR_RELATIVE_MAX_SIZE)
                {
                    PBYTE pNewMemory = NULL;
                    ULONG ulNewLen =
                        LW_MIN( SECURITY_DESCRIPTOR_RELATIVE_MAX_SIZE,
                                pNTTransactState->ulSecurityDescAllocLen + 4096);

                    ntStatus = SrvAllocateMemory(ulNewLen,
                                                 OUT_PPVOID(&pNewMemory));
                    BAIL_ON_NT_STATUS(ntStatus);

                    if (pNTTransactState->pSecurityDescriptor2)
                    {
                        SrvFreeMemory(pNTTransactState->pSecurityDescriptor2);
                    }

                    pNTTransactState->pSecurityDescriptor2 = pNewMemory;
                    pNTTransactState->ulSecurityDescAllocLen = ulNewLen;
                }
                else
                {
                    ntStatus = STATUS_INSUFFICIENT_RESOURCES;
                }
                BAIL_ON_NT_STATUS(ntStatus);

                SrvPrepareExecContextAsync(pExecContext);

                ntStatus = IoQuerySecurityFile(
                                pNTTransactState->pFile->hFile,
                                pExecContext->pAcb,
                                &pExecContext->ioStatusBlock,
                                pNTTransactState->pSecurityRequestHeader->ulSecurityInfo,
                                (PSECURITY_DESCRIPTOR_RELATIVE)pNTTransactState->pSecurityDescriptor2,
                                &pNTTransactState->ulSecurityDescAllocLen);
                switch (ntStatus)
                {
                    case STATUS_SUCCESS:
                    case STATUS_BUFFER_TOO_SMALL:

                        // completed synchronously
                        SrvReleaseExecContextAsync(pExecContext);

                        break;

                    default:

                        BAIL_ON_NT_STATUS(ntStatus);
                }

                break;

            case STATUS_SUCCESS:

                if (!pNTTransactState->pSecurityDescriptor2)
                {
                    pExecContext->ioStatusBlock.Status =
                                                    STATUS_BUFFER_TOO_SMALL;
                }
                else
                {
                    pNTTransactState->ulSecurityDescActualLen =
                            pExecContext->ioStatusBlock.BytesTransferred;

                    bContinue = FALSE;
                }

                break;

            default:

                BAIL_ON_NT_STATUS(ntStatus);

                break;
        }

    } while (bContinue);

error:

    return ntStatus;
}

static
NTSTATUS
SrvBuildQuerySecurityDescriptorResponse(
    PSRV_EXEC_CONTEXT pExecContext
    )
{
    NTSTATUS                   ntStatus     = STATUS_SUCCESS;
    PLWIO_SRV_CONNECTION       pConnection  = pExecContext->pConnection;
    PSRV_EXEC_CONTEXT_SMB_V1   pCtxSmb1     = pExecContext->pProtocolContext;
    ULONG                      iMsg         = pCtxSmb1->iMsg;
    PSRV_MESSAGE_SMB_V1        pSmbRequest  = &pCtxSmb1->pRequests[iMsg];
    PSRV_MESSAGE_SMB_V1        pSmbResponse = &pCtxSmb1->pResponses[iMsg];
    PBYTE   pOutBuffer         = pSmbResponse->pBuffer;
    ULONG   ulBytesAvailable   = pSmbResponse->ulBytesAvailable;
    ULONG   ulOffset           = 0;
    ULONG   ulPackageBytesUsed = 0;
    ULONG   ulTotalBytesUsed   = 0;
    PUSHORT pSetup             = NULL;
    UCHAR   ucSetupCount       = 0;
    ULONG   ulDataOffset       = 0;
    ULONG   ulParameterOffset  = 0;
    PSRV_NTTRANSACT_STATE_SMB_V1 pNTTransactState = pCtxSmb1->hState;

    if (!pSmbResponse->ulSerialNum)
    {
        ntStatus = SrvMarshalHeader_SMB_V1(
                        pOutBuffer,
                        ulOffset,
                        ulBytesAvailable,
                        COM_NT_TRANSACT,
                        (pNTTransactState->ulSecurityDescActualLen <= pNTTransactState->pRequestHeader->ulMaxDataCount ?
                                STATUS_SUCCESS : STATUS_BUFFER_TOO_SMALL),
                        TRUE,
                        (pSmbRequest->pHeader->flags2 & FLAG2_ERR_STATUS) != 0,
                        pConnection->serverProperties.ulCapabilities,
                        pNTTransactState->pTree->tid,
                        SMB_V1_GET_PROCESS_ID(pSmbRequest->pHeader),
                        pNTTransactState->pSession->uid,
                        pSmbRequest->pHeader->mid,
                        &pSmbResponse->pHeader,
                        &pSmbResponse->pWordCount,
                        &pSmbResponse->pAndXHeader,
                        &pSmbResponse->usHeaderSize);
    }
    else
    {
        ntStatus = SrvMarshalHeaderAndX_SMB_V1(
                        pOutBuffer,
                        ulOffset,
                        ulBytesAvailable,
                        COM_NT_TRANSACT,
                        &pSmbResponse->pWordCount,
                        &pSmbResponse->pAndXHeader,
                        &pSmbResponse->usHeaderSize);
    }
    BAIL_ON_NT_STATUS(ntStatus);

    pOutBuffer       += pSmbResponse->usHeaderSize;
    ulOffset         += pSmbResponse->usHeaderSize;
    ulBytesAvailable -= pSmbResponse->usHeaderSize;
    ulTotalBytesUsed += pSmbResponse->usHeaderSize;

    *pSmbResponse->pWordCount = 18 + ucSetupCount;

    ntStatus = WireMarshallNtTransactionResponse(
                    pOutBuffer,
                    ulBytesAvailable,
                    ulOffset,
                    pSetup,
                    ucSetupCount,
                    (PBYTE)&pNTTransactState->ulSecurityDescActualLen,
                    sizeof(pNTTransactState->ulSecurityDescActualLen),
                    (pNTTransactState->ulSecurityDescActualLen <= pNTTransactState->pRequestHeader->ulMaxDataCount ? pNTTransactState->pSecurityDescriptor2 : NULL),
                    (pNTTransactState->ulSecurityDescActualLen <= pNTTransactState->pRequestHeader->ulMaxDataCount ? pNTTransactState->ulSecurityDescActualLen : 0),
                    &ulDataOffset,
                    &ulParameterOffset,
                    &ulPackageBytesUsed);
    BAIL_ON_NT_STATUS(ntStatus);

    // pOutBuffer       += ulPackageBytesUsed;
    // ulOffset         += ulPackageBytesUsed;
    // ulBytesAvailable -= ulPackageBytesUsed;
    ulTotalBytesUsed += ulPackageBytesUsed;

    pSmbResponse->ulMessageSize = ulTotalBytesUsed;

cleanup:

    return ntStatus;

error:

    if (ulTotalBytesUsed)
    {
        pSmbResponse->pHeader = NULL;
        pSmbResponse->pAndXHeader = NULL;
        memset(pSmbResponse->pBuffer, 0, ulTotalBytesUsed);
    }

    pSmbResponse->ulMessageSize = 0;

    goto cleanup;
}

static
NTSTATUS
SrvSetSecurityDescriptor(
    PSRV_EXEC_CONTEXT pExecContext
    )
{
    NTSTATUS                   ntStatus     = STATUS_SUCCESS;
    PSRV_EXEC_CONTEXT_SMB_V1   pCtxSmb1     = pExecContext->pProtocolContext;
    PSRV_NTTRANSACT_STATE_SMB_V1     pNTTransactState    = pCtxSmb1->hState;

    switch (pNTTransactState->stage)
    {
        case SRV_NTTRANSACT_STAGE_SMB_V1_INITIAL:

            if (pNTTransactState->pRequestHeader->ulTotalParameterCount !=
                                    sizeof(SMB_SECURITY_INFORMATION_HEADER))
            {
                ntStatus = STATUS_INVALID_NETWORK_RESPONSE;
                BAIL_ON_NT_STATUS(ntStatus);
            }

            pNTTransactState->pSecurityRequestHeader =
                (PSMB_SECURITY_INFORMATION_HEADER)pNTTransactState->pParameters;

            SRV_LOG_DEBUG(
                    pExecContext->pLogContext,
                    SMB_PROTOCOL_VERSION_1,
                    pCtxSmb1->pRequests[pCtxSmb1->iMsg].pHeader->command,
                    "NT Transact (Set Sec Desc): "
                    "command(%u),uid(%u),mid(%u),pid(%u),tid(%u),"
                    "file-id(%u),sec-info(%u)",
                    pCtxSmb1->pRequests[pCtxSmb1->iMsg].pHeader->command,
                    pCtxSmb1->pRequests[pCtxSmb1->iMsg].pHeader->uid,
                    pCtxSmb1->pRequests[pCtxSmb1->iMsg].pHeader->mid,
                    SMB_V1_GET_PROCESS_ID(pCtxSmb1->pRequests[pCtxSmb1->iMsg].pHeader),
                    pCtxSmb1->pRequests[pCtxSmb1->iMsg].pHeader->tid,
                    pNTTransactState->pSecurityRequestHeader->usFid,
                    pNTTransactState->pSecurityRequestHeader->ulSecurityInfo);

            ntStatus = SrvTreeFindFile_SMB_V1(
                            pCtxSmb1,
                            pNTTransactState->pTree,
                            pNTTransactState->pSecurityRequestHeader->usFid,
                            &pNTTransactState->pFile);
            BAIL_ON_NT_STATUS(ntStatus);

            pNTTransactState->stage = SRV_NTTRANSACT_STAGE_SMB_V1_ATTEMPT_IO;

            // intentional fall through

        case SRV_NTTRANSACT_STAGE_SMB_V1_ATTEMPT_IO:

            pNTTransactState->stage = SRV_NTTRANSACT_STAGE_SMB_V1_BUILD_RESPONSE;

            SrvPrepareExecContextAsync(pExecContext);

            ntStatus = IoSetSecurityFile(
                            pNTTransactState->pFile->hFile,
                            pExecContext->pAcb,
                            &pExecContext->ioStatusBlock,
                            pNTTransactState->pSecurityRequestHeader->ulSecurityInfo,
                            (PSECURITY_DESCRIPTOR_RELATIVE)pNTTransactState->pData,
                            &pNTTransactState->pRequestHeader->ulTotalDataCount);
            BAIL_ON_NT_STATUS(ntStatus);

            // completed synchronously
            SrvReleaseExecContextAsync(pExecContext);

            // intentional fall through

        case SRV_NTTRANSACT_STAGE_SMB_V1_BUILD_RESPONSE:

            ntStatus = pExecContext->ioStatusBlock.Status;
            BAIL_ON_NT_STATUS(ntStatus);

            ntStatus = SrvBuildSetSecurityDescriptorResponse(pExecContext);
            BAIL_ON_NT_STATUS(ntStatus);

            pNTTransactState->stage = SRV_NTTRANSACT_STAGE_SMB_V1_DONE;

            // intentional fall through

        case SRV_NTTRANSACT_STAGE_SMB_V1_DONE:

            break;

        case SRV_NTTRANSACT_STAGE_SMB_V1_CREATE_COMPLETED:
        case SRV_NTTRANSACT_STAGE_SMB_V1_QUERY_INFO:
        case SRV_NTTRANSACT_STAGE_SMB_V1_GET_OPLOCKS:

            ntStatus = STATUS_INTERNAL_ERROR;

            break;
    }

error:

    return ntStatus;
}

static
NTSTATUS
SrvBuildSetSecurityDescriptorResponse(
    PSRV_EXEC_CONTEXT pExecContext
    )
{
    NTSTATUS                   ntStatus     = STATUS_SUCCESS;
    PLWIO_SRV_CONNECTION       pConnection  = pExecContext->pConnection;
    PSRV_EXEC_CONTEXT_SMB_V1   pCtxSmb1     = pExecContext->pProtocolContext;
    ULONG                      iMsg         = pCtxSmb1->iMsg;
    PSRV_MESSAGE_SMB_V1        pSmbRequest  = &pCtxSmb1->pRequests[iMsg];
    PSRV_MESSAGE_SMB_V1        pSmbResponse = &pCtxSmb1->pResponses[iMsg];
    PBYTE   pOutBuffer         = pSmbResponse->pBuffer;
    ULONG   ulBytesAvailable   = pSmbResponse->ulBytesAvailable;
    ULONG   ulOffset           = 0;
    ULONG   ulPackageBytesUsed = 0;
    ULONG   ulTotalBytesUsed   = 0;
    PUSHORT pSetup             = NULL;
    UCHAR   ucSetupCount       = 0;
    ULONG   ulDataOffset       = 0;
    ULONG   ulParameterOffset  = 0;
    PSRV_NTTRANSACT_STATE_SMB_V1 pNTTransactState = pCtxSmb1->hState;

    if (!pSmbResponse->ulSerialNum)
    {
        ntStatus = SrvMarshalHeader_SMB_V1(
                        pOutBuffer,
                        ulOffset,
                        ulBytesAvailable,
                        COM_NT_TRANSACT,
                        STATUS_SUCCESS,
                        TRUE,
                        (pSmbRequest->pHeader->flags2 & FLAG2_ERR_STATUS) != 0,
                        pConnection->serverProperties.ulCapabilities,
                        pNTTransactState->pTree->tid,
                        SMB_V1_GET_PROCESS_ID(pSmbRequest->pHeader),
                        pNTTransactState->pSession->uid,
                        pSmbRequest->pHeader->mid,
                        &pSmbResponse->pHeader,
                        &pSmbResponse->pWordCount,
                        &pSmbResponse->pAndXHeader,
                        &pSmbResponse->usHeaderSize);
    }
    else
    {
        ntStatus = SrvMarshalHeaderAndX_SMB_V1(
                        pOutBuffer,
                        ulOffset,
                        ulBytesAvailable,
                        COM_NT_TRANSACT,
                        &pSmbResponse->pWordCount,
                        &pSmbResponse->pAndXHeader,
                        &pSmbResponse->usHeaderSize);
    }
    BAIL_ON_NT_STATUS(ntStatus);

    pOutBuffer       += pSmbResponse->usHeaderSize;
    ulOffset         += pSmbResponse->usHeaderSize;
    ulBytesAvailable -= pSmbResponse->usHeaderSize;
    ulTotalBytesUsed += pSmbResponse->usHeaderSize;

    *pSmbResponse->pWordCount = 18 + ucSetupCount;

    ntStatus = WireMarshallNtTransactionResponse(
                    pOutBuffer,
                    ulBytesAvailable,
                    ulOffset,
                    pSetup,
                    ucSetupCount,
                    NULL,
                    0,
                    NULL,
                    0,
                    &ulDataOffset,
                    &ulParameterOffset,
                    &ulPackageBytesUsed);
    BAIL_ON_NT_STATUS(ntStatus);

    // pOutBuffer       += ulPackageBytesUsed;
    // ulOffset         += ulPackageBytesUsed;
    // ulBytesAvailable -= ulPackageBytesUsed;
    ulTotalBytesUsed += ulPackageBytesUsed;

    pSmbResponse->ulMessageSize = ulTotalBytesUsed;

cleanup:

    return ntStatus;

error:

    if (ulTotalBytesUsed)
    {
        pSmbResponse->pHeader = NULL;
        pSmbResponse->pAndXHeader = NULL;
        memset(pSmbResponse->pBuffer, 0, ulTotalBytesUsed);
    }

    pSmbResponse->ulMessageSize = 0;

    goto cleanup;
}

static
NTSTATUS
SrvProcessIOCTL(
    PSRV_EXEC_CONTEXT pExecContext
    )
{
    NTSTATUS                   ntStatus     = STATUS_SUCCESS;
    PSRV_EXEC_CONTEXT_SMB_V1   pCtxSmb1     = pExecContext->pProtocolContext;
    PSRV_NTTRANSACT_STATE_SMB_V1     pNTTransactState    = pCtxSmb1->hState;

    switch (pNTTransactState->stage)
    {
        case SRV_NTTRANSACT_STAGE_SMB_V1_INITIAL:

            if (pNTTransactState->pSetup)
            {
                if (sizeof(SMB_IOCTL_HEADER) !=
                        pNTTransactState->pRequestHeader->ucSetupCount * sizeof(USHORT))
                {
                    ntStatus = STATUS_INVALID_NETWORK_RESPONSE;
                    BAIL_ON_NT_STATUS(ntStatus);
                }

                pNTTransactState->pIoctlRequest =
                        (PSMB_IOCTL_HEADER)((PBYTE)pNTTransactState->pSetup);

            }
            else if (pNTTransactState->pRequestHeader->ulTotalParameterCount ==
                                                    sizeof(SMB_IOCTL_HEADER))
            {
                pNTTransactState->pIoctlRequest =
                        (PSMB_IOCTL_HEADER)pNTTransactState->pParameters;
            }
            else
            {
                ntStatus = STATUS_INVALID_NETWORK_RESPONSE;
                BAIL_ON_NT_STATUS(ntStatus);
            }

            SrvUnmarshallBoolean(&pNTTransactState->pIoctlRequest->bIsFsctl);

            SRV_LOG_DEBUG(
                    pExecContext->pLogContext,
                    SMB_PROTOCOL_VERSION_1,
                    pCtxSmb1->pRequests[pCtxSmb1->iMsg].pHeader->command,
                    "NT Transact (IOCTL): "
                    "command(%u),uid(%u),mid(%u),pid(%u),tid(%u),"
                    "file-id(%u),is-fsctl(%s),flags(0x%x),function-code(%u)",
                    pCtxSmb1->pRequests[pCtxSmb1->iMsg].pHeader->command,
                    pCtxSmb1->pRequests[pCtxSmb1->iMsg].pHeader->uid,
                    pCtxSmb1->pRequests[pCtxSmb1->iMsg].pHeader->mid,
                    SMB_V1_GET_PROCESS_ID(pCtxSmb1->pRequests[pCtxSmb1->iMsg].pHeader),
                    pCtxSmb1->pRequests[pCtxSmb1->iMsg].pHeader->tid,
                    pNTTransactState->pIoctlRequest->usFid,
                    pNTTransactState->pIoctlRequest->bIsFsctl? "TRUE" : "FALSE",
                    pNTTransactState->pIoctlRequest->ucFlags,
                    pNTTransactState->pIoctlRequest->ulFunctionCode);

            if (pNTTransactState->pIoctlRequest->ucFlags & 0x1)
            {
                // TODO: Apply only to DFS Share
                //       We don't support DFS yet
                ntStatus = STATUS_NOT_SUPPORTED;
                BAIL_ON_NT_STATUS(ntStatus);
            }

            ntStatus = SrvTreeFindFile_SMB_V1(
                            pCtxSmb1,
                            pNTTransactState->pTree,
                            pNTTransactState->pIoctlRequest->usFid,
                            &pNTTransactState->pFile);
            BAIL_ON_NT_STATUS(ntStatus);

            pNTTransactState->stage = SRV_NTTRANSACT_STAGE_SMB_V1_ATTEMPT_IO;

            // intentional fall through

        case SRV_NTTRANSACT_STAGE_SMB_V1_ATTEMPT_IO:

            if (pExecContext->pStatInfo)
            {
                ntStatus = SrvStatisticsSetIOCTL(
                                pExecContext->pStatInfo,
                                pNTTransactState->pIoctlRequest->ulFunctionCode);
                BAIL_ON_NT_STATUS(ntStatus);
            }

            if (pNTTransactState->pIoctlRequest->bIsFsctl &&
                (pNTTransactState->pIoctlRequest->ulFunctionCode ==
                     IO_FSCTL_SRV_ENUMERATE_SNAPSHOTS))
            {
                ntStatus = SrvExecuteEnumerateSnapshots(pExecContext);
                BAIL_ON_NT_STATUS(ntStatus);
            }
            else
            {
                ntStatus = SrvExecuteIoctl(pExecContext);
                BAIL_ON_NT_STATUS(ntStatus);
            }
            pNTTransactState->stage = SRV_NTTRANSACT_STAGE_SMB_V1_BUILD_RESPONSE;

            // intentional fall through

        case SRV_NTTRANSACT_STAGE_SMB_V1_BUILD_RESPONSE:

            ntStatus = SrvBuildIOCTLResponse(pExecContext);
            BAIL_ON_NT_STATUS(ntStatus);

            pNTTransactState->stage = SRV_NTTRANSACT_STAGE_SMB_V1_DONE;

            // intentional fall through

        case SRV_NTTRANSACT_STAGE_SMB_V1_DONE:

            break;

        case SRV_NTTRANSACT_STAGE_SMB_V1_CREATE_COMPLETED:
        case SRV_NTTRANSACT_STAGE_SMB_V1_QUERY_INFO:
        case SRV_NTTRANSACT_STAGE_SMB_V1_GET_OPLOCKS:

            ntStatus = STATUS_INTERNAL_ERROR;

            break;
    }

error:

    return ntStatus;
}

static
NTSTATUS
SrvProcessNtTransactQueryQuota(
    PSRV_EXEC_CONTEXT pExecContext
    )
{
    NTSTATUS                   ntStatus     = STATUS_SUCCESS;
    PSRV_EXEC_CONTEXT_SMB_V1   pCtxSmb1     = pExecContext->pProtocolContext;
    PSRV_NTTRANSACT_STATE_SMB_V1 pNTTransactState = pCtxSmb1->hState;
    PSMB_QUERY_QUOTA_HEADER    pQueryQuotaHeader = NULL;

    // pNTTransactState->pSetup is ignored here

    if (pNTTransactState->pRequestHeader->ulTotalParameterCount !=
                                        sizeof(SMB_QUERY_QUOTA_HEADER))
    {
        ntStatus = STATUS_INVALID_NETWORK_RESPONSE;
        BAIL_ON_NT_STATUS(ntStatus);
    }

    pQueryQuotaHeader = (PSMB_QUERY_QUOTA_HEADER)pNTTransactState->pParameters;

    switch (pNTTransactState->stage)
    {
        case SRV_NTTRANSACT_STAGE_SMB_V1_INITIAL:

            SrvUnmarshallBoolean(&pQueryQuotaHeader->bRestartScan);
            SrvUnmarshallBoolean(&pQueryQuotaHeader->bReturnSingleEntry);

            SRV_LOG_DEBUG(
                    pExecContext->pLogContext,
                    SMB_PROTOCOL_VERSION_1,
                    pCtxSmb1->pRequests[pCtxSmb1->iMsg].pHeader->command,
                    "NT Transact (QUERY_QUOTA): "
                    "command(%u),uid(%u),mid(%u),pid(%u),tid(%u),"
                    "file-id(%u),is-return-single-entry(%s),is-restart-scan(%s),"
                    "sid-list-len(%u),start-sid-len(%u),start-sid-offset(%u)",
                    pCtxSmb1->pRequests[pCtxSmb1->iMsg].pHeader->command,
                    pCtxSmb1->pRequests[pCtxSmb1->iMsg].pHeader->uid,
                    pCtxSmb1->pRequests[pCtxSmb1->iMsg].pHeader->mid,
                    SMB_V1_GET_PROCESS_ID(pCtxSmb1->pRequests[pCtxSmb1->iMsg].pHeader),
                    pCtxSmb1->pRequests[pCtxSmb1->iMsg].pHeader->tid,
                    pQueryQuotaHeader->usFid,
                    pQueryQuotaHeader->bReturnSingleEntry ? "TRUE" : "FALSE",
                    pQueryQuotaHeader->bRestartScan ? "TRUE" : "FALSE",
                    pQueryQuotaHeader->ulSidListLength,
                    pQueryQuotaHeader->ulStartSidLength,
                    pQueryQuotaHeader->ulStartSidOffset);

            ntStatus = SrvTreeFindFile_SMB_V1(
                            pCtxSmb1,
                            pNTTransactState->pTree,
                            pQueryQuotaHeader->usFid,
                            &pNTTransactState->pFile);
            BAIL_ON_NT_STATUS(ntStatus);

            pNTTransactState->stage = SRV_NTTRANSACT_STAGE_SMB_V1_ATTEMPT_IO;

            // intentional fall through

        case SRV_NTTRANSACT_STAGE_SMB_V1_ATTEMPT_IO:

            pNTTransactState->stage = SRV_NTTRANSACT_STAGE_SMB_V1_BUILD_RESPONSE;

            ntStatus = SrvExecuteQueryQuota(pExecContext);
            BAIL_ON_NT_STATUS(ntStatus);

            // intentional fall through

        case SRV_NTTRANSACT_STAGE_SMB_V1_BUILD_RESPONSE:

            ntStatus = pExecContext->ioStatusBlock.Status;
            BAIL_ON_NT_STATUS(ntStatus);

            ntStatus = SrvBuildQueryQuotaResponse(pExecContext);
            BAIL_ON_NT_STATUS(ntStatus);

            pNTTransactState->stage = SRV_NTTRANSACT_STAGE_SMB_V1_DONE;

            // intentional fall through

        case SRV_NTTRANSACT_STAGE_SMB_V1_DONE:

            break;

        case SRV_NTTRANSACT_STAGE_SMB_V1_CREATE_COMPLETED:
        case SRV_NTTRANSACT_STAGE_SMB_V1_QUERY_INFO:
        case SRV_NTTRANSACT_STAGE_SMB_V1_GET_OPLOCKS:

            ntStatus = STATUS_INTERNAL_ERROR;

            break;
    }

error:

    return ntStatus;
}

static
NTSTATUS
SrvExecuteQueryQuota(
    PSRV_EXEC_CONTEXT pExecContext
    )
{
    NTSTATUS                     ntStatus     = STATUS_SUCCESS;
    PSRV_EXEC_CONTEXT_SMB_V1     pCtxSmb1     = pExecContext->pProtocolContext;
    PSRV_NTTRANSACT_STATE_SMB_V1 pNTTransactState = pCtxSmb1->hState;
    PSMB_QUERY_QUOTA_HEADER      pQueryQuotaHeader = NULL;
    PBYTE                        pStartSid = NULL;

    pQueryQuotaHeader = (PSMB_QUERY_QUOTA_HEADER)pNTTransactState->pParameters;

    if (!pNTTransactState->pRequestHeader->ulMaxDataCount ||
        pNTTransactState->pRequestHeader->ulMaxDataCount > UINT16_MAX)
    {
        ntStatus = STATUS_INVALID_PARAMETER;
        BAIL_ON_NT_STATUS(ntStatus);
    }

    if (!pNTTransactState->pResponseBuffer)
    {
        ntStatus = SrvAllocateMemory(
                        pNTTransactState->pRequestHeader->ulMaxDataCount,
                        OUT_PPVOID(&pNTTransactState->pResponseBuffer));
        BAIL_ON_NT_STATUS(ntStatus);

        pNTTransactState->usResponseBufferLen =
                        pNTTransactState->pRequestHeader->ulMaxDataCount;
    }
    else
    {
        ntStatus = STATUS_INTERNAL_ERROR;
        BAIL_ON_NT_STATUS(ntStatus);
    }

    if (pQueryQuotaHeader->ulStartSidLength != 0)
    {
        pStartSid = pNTTransactState->pData + pQueryQuotaHeader->ulStartSidOffset;
    }

    SrvPrepareExecContextAsync(pExecContext);

    ntStatus = IoQueryQuotaInformationFile(
                        pNTTransactState->pFile->hFile,
                        pExecContext->pAcb,
                        &pExecContext->ioStatusBlock,
                        (PFILE_QUOTA_INFORMATION)pNTTransactState->pResponseBuffer,
                        pNTTransactState->usResponseBufferLen,
                        pQueryQuotaHeader->bReturnSingleEntry,
                        (PFILE_GET_QUOTA_INFORMATION)pNTTransactState->pData,
                        pQueryQuotaHeader->ulSidListLength,
                        (PFILE_GET_QUOTA_INFORMATION)pStartSid,
                        pQueryQuotaHeader->bRestartScan);
    switch (ntStatus)
    {
        case STATUS_SUCCESS:

            // completed synchronously
            SrvReleaseExecContextAsync(pExecContext);

            break;

        default:

            BAIL_ON_NT_STATUS(ntStatus);

            break;
    }

    pNTTransactState->usActualResponseLen =
                            pExecContext->ioStatusBlock.BytesTransferred;

error:

    return ntStatus;
}

static
NTSTATUS
SrvBuildQueryQuotaResponse(
    PSRV_EXEC_CONTEXT pExecContext
    )
{
    NTSTATUS                     ntStatus     = STATUS_SUCCESS;
    PLWIO_SRV_CONNECTION         pConnection  = pExecContext->pConnection;
    PSRV_EXEC_CONTEXT_SMB_V1     pCtxSmb1     = pExecContext->pProtocolContext;
    PSRV_NTTRANSACT_STATE_SMB_V1 pNTTransactState    = pCtxSmb1->hState;
    ULONG                        iMsg         = pCtxSmb1->iMsg;
    PSRV_MESSAGE_SMB_V1          pSmbRequest  = &pCtxSmb1->pRequests[iMsg];
    PSRV_MESSAGE_SMB_V1          pSmbResponse = &pCtxSmb1->pResponses[iMsg];
    PBYTE pOutBuffer           = pSmbResponse->pBuffer;
    ULONG ulBytesAvailable     = pSmbResponse->ulBytesAvailable;
    ULONG ulOffset             = 0;
    ULONG ulPackageBytesUsed   = 0;
    ULONG ulTotalBytesUsed     = 0;
    UCHAR ucResponseSetupCount = 0;
    ULONG ulDataOffset         = 0;
    ULONG ulParameterOffset    = 0;
    ULONG ulParams             = 0;

    if (!pSmbResponse->ulSerialNum)
    {
        ntStatus = SrvMarshalHeader_SMB_V1(
                        pOutBuffer,
                        ulOffset,
                        ulBytesAvailable,
                        COM_NT_TRANSACT,
                        STATUS_SUCCESS,
                        TRUE,
                        (pSmbRequest->pHeader->flags2 & FLAG2_ERR_STATUS) != 0,
                        pConnection->serverProperties.ulCapabilities,
                        pNTTransactState->pTree->tid,
                        SMB_V1_GET_PROCESS_ID(pSmbRequest->pHeader),
                        pNTTransactState->pSession->uid,
                        pSmbRequest->pHeader->mid,
                        &pSmbResponse->pHeader,
                        &pSmbResponse->pWordCount,
                        &pSmbResponse->pAndXHeader,
                        &pSmbResponse->usHeaderSize);
    }
    else
    {
        ntStatus = SrvMarshalHeaderAndX_SMB_V1(
                        pOutBuffer,
                        ulOffset,
                        ulBytesAvailable,
                        COM_NT_TRANSACT,
                        &pSmbResponse->pWordCount,
                        &pSmbResponse->pAndXHeader,
                        &pSmbResponse->usHeaderSize);
    }
    BAIL_ON_NT_STATUS(ntStatus);

    pOutBuffer       += pSmbResponse->usHeaderSize;
    ulOffset         += pSmbResponse->usHeaderSize;
    ulBytesAvailable -= pSmbResponse->usHeaderSize;
    ulTotalBytesUsed += pSmbResponse->usHeaderSize;

    *pSmbResponse->pWordCount = 18 + ucResponseSetupCount;
    ulParams = pNTTransactState->usActualResponseLen;

    ntStatus = WireMarshallNtTransactionResponse(
                    pOutBuffer,
                    ulBytesAvailable,
                    ulOffset,
                    NULL,
                    ucResponseSetupCount,
                    (PBYTE)&ulParams,
                    sizeof(ulParams),
                    pNTTransactState->pResponseBuffer,
                    pExecContext->ioStatusBlock.BytesTransferred,
                    &ulDataOffset,
                    &ulParameterOffset,
                    &ulPackageBytesUsed);
    BAIL_ON_NT_STATUS(ntStatus);

    // pOutBuffer       += ulPackageBytesUsed;
    // ulOffset         += ulPackageBytesUsed;
    // ulBytesAvailable -= ulPackageBytesUsed;
    ulTotalBytesUsed += ulPackageBytesUsed;

    pSmbResponse->ulMessageSize = ulTotalBytesUsed;

cleanup:

    return ntStatus;

error:

    if (ulTotalBytesUsed)
    {
        pSmbResponse->pHeader = NULL;
        pSmbResponse->pAndXHeader = NULL;
        memset(pSmbResponse->pBuffer, 0, ulTotalBytesUsed);
    }

    pSmbResponse->ulMessageSize = 0;

    goto cleanup;
}

static
NTSTATUS
SrvProcessNtTransactSetQuota(
    PSRV_EXEC_CONTEXT pExecContext
    )
{
    NTSTATUS                   ntStatus = STATUS_SUCCESS;
    PSRV_EXEC_CONTEXT_SMB_V1   pCtxSmb1 = pExecContext->pProtocolContext;
    PSRV_NTTRANSACT_STATE_SMB_V1 pNTTransactState = pCtxSmb1->hState;
    PSMB_SET_QUOTA_HEADER      pSetQuotaHeader = NULL;

    if (pNTTransactState->pRequestHeader->ulTotalParameterCount !=
                                        sizeof(SMB_SET_QUOTA_HEADER))
    {
        ntStatus = STATUS_INVALID_NETWORK_RESPONSE;
        BAIL_ON_NT_STATUS(ntStatus);
    }

    pSetQuotaHeader = (PSMB_SET_QUOTA_HEADER)pNTTransactState->pParameters;

    switch (pNTTransactState->stage)
    {
        case SRV_NTTRANSACT_STAGE_SMB_V1_INITIAL:

            SRV_LOG_DEBUG(
                    pExecContext->pLogContext,
                    SMB_PROTOCOL_VERSION_1,
                    pCtxSmb1->pRequests[pCtxSmb1->iMsg].pHeader->command,
                    "NT Transact (SET_QUOTA): "
                    "command(%u),uid(%u),mid(%u),pid(%u),tid(%u),"
                    "file-id(%u)",
                    pCtxSmb1->pRequests[pCtxSmb1->iMsg].pHeader->command,
                    pCtxSmb1->pRequests[pCtxSmb1->iMsg].pHeader->uid,
                    pCtxSmb1->pRequests[pCtxSmb1->iMsg].pHeader->mid,
                    SMB_V1_GET_PROCESS_ID(pCtxSmb1->pRequests[pCtxSmb1->iMsg].pHeader),
                    pCtxSmb1->pRequests[pCtxSmb1->iMsg].pHeader->tid,
                    pSetQuotaHeader->usFid);

            ntStatus = SrvTreeFindFile_SMB_V1(
                            pCtxSmb1,
                            pNTTransactState->pTree,
                            pSetQuotaHeader->usFid,
                            &pNTTransactState->pFile);
            BAIL_ON_NT_STATUS(ntStatus);

            pNTTransactState->stage = SRV_NTTRANSACT_STAGE_SMB_V1_ATTEMPT_IO;

            // intentional fall through

        case SRV_NTTRANSACT_STAGE_SMB_V1_ATTEMPT_IO:

            pNTTransactState->stage = SRV_NTTRANSACT_STAGE_SMB_V1_BUILD_RESPONSE;

            ntStatus = SrvExecuteSetQuota(pExecContext);
            BAIL_ON_NT_STATUS(ntStatus);

            // intentional fall through

        case SRV_NTTRANSACT_STAGE_SMB_V1_BUILD_RESPONSE:

            ntStatus = pExecContext->ioStatusBlock.Status;
            BAIL_ON_NT_STATUS(ntStatus);

            ntStatus = SrvBuildSetQuotaResponse(pExecContext);
            BAIL_ON_NT_STATUS(ntStatus);

            pNTTransactState->stage = SRV_NTTRANSACT_STAGE_SMB_V1_DONE;

            // intentional fall through

        case SRV_NTTRANSACT_STAGE_SMB_V1_DONE:

            break;

        case SRV_NTTRANSACT_STAGE_SMB_V1_CREATE_COMPLETED:
        case SRV_NTTRANSACT_STAGE_SMB_V1_QUERY_INFO:
        case SRV_NTTRANSACT_STAGE_SMB_V1_GET_OPLOCKS:

            ntStatus = STATUS_INTERNAL_ERROR;

            break;
    }

error:

    return ntStatus;
}

static
NTSTATUS
SrvExecuteSetQuota(
    PSRV_EXEC_CONTEXT pExecContext
    )
{
    NTSTATUS                     ntStatus     = STATUS_SUCCESS;
    PSRV_EXEC_CONTEXT_SMB_V1     pCtxSmb1     = pExecContext->pProtocolContext;
    PSRV_NTTRANSACT_STATE_SMB_V1 pNTTransactState = pCtxSmb1->hState;

    SrvPrepareExecContextAsync(pExecContext);

    ntStatus = IoSetQuotaInformationFile(
                        pNTTransactState->pFile->hFile,
                        pExecContext->pAcb,
                        &pExecContext->ioStatusBlock,
                        (PFILE_QUOTA_INFORMATION)pNTTransactState->pData,
                        pNTTransactState->pRequestHeader->ulDataCount);
    switch (ntStatus)
    {
        case STATUS_SUCCESS:

            // completed synchronously
            SrvReleaseExecContextAsync(pExecContext);

            break;

        default:

            BAIL_ON_NT_STATUS(ntStatus);

            break;
    }

    pNTTransactState->usActualResponseLen =
                            pExecContext->ioStatusBlock.BytesTransferred;

error:

    return ntStatus;
}

static
NTSTATUS
SrvBuildSetQuotaResponse(
    PSRV_EXEC_CONTEXT pExecContext
    )
{
    NTSTATUS                     ntStatus     = STATUS_SUCCESS;
    PLWIO_SRV_CONNECTION         pConnection  = pExecContext->pConnection;
    PSRV_EXEC_CONTEXT_SMB_V1     pCtxSmb1     = pExecContext->pProtocolContext;
    PSRV_NTTRANSACT_STATE_SMB_V1 pNTTransactState    = pCtxSmb1->hState;
    ULONG                        iMsg         = pCtxSmb1->iMsg;
    PSRV_MESSAGE_SMB_V1          pSmbRequest  = &pCtxSmb1->pRequests[iMsg];
    PSRV_MESSAGE_SMB_V1          pSmbResponse = &pCtxSmb1->pResponses[iMsg];
    PBYTE pOutBuffer           = pSmbResponse->pBuffer;
    ULONG ulBytesAvailable     = pSmbResponse->ulBytesAvailable;
    ULONG ulOffset             = 0;
    ULONG ulPackageBytesUsed   = 0;
    ULONG ulTotalBytesUsed     = 0;
    UCHAR ucResponseSetupCount = 0;
    ULONG ulDataOffset         = 0;
    ULONG ulParameterOffset    = 0;

    if (!pSmbResponse->ulSerialNum)
    {
        ntStatus = SrvMarshalHeader_SMB_V1(
                        pOutBuffer,
                        ulOffset,
                        ulBytesAvailable,
                        COM_NT_TRANSACT,
                        STATUS_SUCCESS,
                        TRUE,
                        (pSmbRequest->pHeader->flags2 & FLAG2_ERR_STATUS) != 0,
                        pConnection->serverProperties.ulCapabilities,
                        pNTTransactState->pTree->tid,
                        SMB_V1_GET_PROCESS_ID(pSmbRequest->pHeader),
                        pNTTransactState->pSession->uid,
                        pSmbRequest->pHeader->mid,
                        &pSmbResponse->pHeader,
                        &pSmbResponse->pWordCount,
                        &pSmbResponse->pAndXHeader,
                        &pSmbResponse->usHeaderSize);
    }
    else
    {
        ntStatus = SrvMarshalHeaderAndX_SMB_V1(
                        pOutBuffer,
                        ulOffset,
                        ulBytesAvailable,
                        COM_NT_TRANSACT,
                        &pSmbResponse->pWordCount,
                        &pSmbResponse->pAndXHeader,
                        &pSmbResponse->usHeaderSize);
    }
    BAIL_ON_NT_STATUS(ntStatus);

    pOutBuffer       += pSmbResponse->usHeaderSize;
    ulOffset         += pSmbResponse->usHeaderSize;
    ulBytesAvailable -= pSmbResponse->usHeaderSize;
    ulTotalBytesUsed += pSmbResponse->usHeaderSize;

    *pSmbResponse->pWordCount = 18 + ucResponseSetupCount;

    ntStatus = WireMarshallNtTransactionResponse(
                    pOutBuffer,
                    ulBytesAvailable,
                    ulOffset,
                    &pNTTransactState->usActualResponseLen,
                    ucResponseSetupCount,
                    NULL,
                    0,
                    pNTTransactState->pResponseBuffer,
                    pNTTransactState->usActualResponseLen,
                    &ulDataOffset,
                    &ulParameterOffset,
                    &ulPackageBytesUsed);
    BAIL_ON_NT_STATUS(ntStatus);

    // pOutBuffer       += ulPackageBytesUsed;
    // ulOffset         += ulPackageBytesUsed;
    // ulBytesAvailable -= ulPackageBytesUsed;
    ulTotalBytesUsed += ulPackageBytesUsed;

    pSmbResponse->ulMessageSize = ulTotalBytesUsed;

cleanup:

    return ntStatus;

error:

    if (ulTotalBytesUsed)
    {
        pSmbResponse->pHeader = NULL;
        pSmbResponse->pAndXHeader = NULL;
        memset(pSmbResponse->pBuffer, 0, ulTotalBytesUsed);
    }

    pSmbResponse->ulMessageSize = 0;

    goto cleanup;
}

static
NTSTATUS
SrvExecuteIoctl(
    PSRV_EXEC_CONTEXT pExecContext
    )
{
    NTSTATUS                   ntStatus     = STATUS_SUCCESS;
    PSRV_EXEC_CONTEXT_SMB_V1   pCtxSmb1     = pExecContext->pProtocolContext;
    PSRV_NTTRANSACT_STATE_SMB_V1 pNTTransactState = pCtxSmb1->hState;
    BOOLEAN                    bContinue    = TRUE;

    do
    {
        ntStatus = pExecContext->ioStatusBlock.Status;

        if (STATUS_BUFFER_OVERFLOW == ntStatus)
        {
            ntStatus = STATUS_SUCCESS;
        }

        switch (ntStatus)
        {
            case STATUS_BUFFER_TOO_SMALL:

                if (!pNTTransactState->pResponseBuffer)
                {
                    USHORT usInitialLength =
                        pNTTransactState->pRequestHeader->ulMaxDataCount > 0 ?
                        pNTTransactState->pRequestHeader->ulMaxDataCount : 512;

                    ntStatus = SrvAllocateMemory(
                                usInitialLength,
                                OUT_PPVOID(&pNTTransactState->pResponseBuffer));
                    BAIL_ON_NT_STATUS(ntStatus);

                    pNTTransactState->usResponseBufferLen = usInitialLength;
                }
                else
                {
                    USHORT usNewLength = 0;

                    if ((pNTTransactState->usResponseBufferLen + 256) > UINT16_MAX)
                    {
                        ntStatus = STATUS_INSUFFICIENT_RESOURCES;
                        BAIL_ON_NT_STATUS(ntStatus);
                    }

                    usNewLength = pNTTransactState->usResponseBufferLen + 256;

                    if (pNTTransactState->pResponseBuffer)
                    {
                        SrvFreeMemory(pNTTransactState->pResponseBuffer);
                        pNTTransactState->pResponseBuffer = NULL;
                        pNTTransactState->usResponseBufferLen = 0;
                    }

                    ntStatus = SrvAllocateMemory(
                                usNewLength,
                                OUT_PPVOID(&pNTTransactState->pResponseBuffer));
                    BAIL_ON_NT_STATUS(ntStatus);

                    pNTTransactState->usResponseBufferLen = usNewLength;
                }

                SrvPrepareExecContextAsync(pExecContext);

                if (pNTTransactState->pIoctlRequest->bIsFsctl)
                {
                    ntStatus = IoFsControlFile(
                            pNTTransactState->pFile->hFile,
                            pExecContext->pAcb,
                            &pExecContext->ioStatusBlock,
                            pNTTransactState->pIoctlRequest->ulFunctionCode,
                            pNTTransactState->pData,
                            pNTTransactState->pRequestHeader->ulTotalDataCount,
                            pNTTransactState->pResponseBuffer,
                            pNTTransactState->usResponseBufferLen);
                }
                else
                {
                    ntStatus = IoDeviceIoControlFile(
                            pNTTransactState->pFile->hFile,
                            pExecContext->pAcb,
                            &pExecContext->ioStatusBlock,
                            pNTTransactState->pIoctlRequest->ulFunctionCode,
                            pNTTransactState->pData,
                            pNTTransactState->pRequestHeader->ulTotalDataCount,
                            pNTTransactState->pResponseBuffer,
                            pNTTransactState->usResponseBufferLen);
                }

                switch (ntStatus)
                {
                    case STATUS_SUCCESS:
                    case STATUS_BUFFER_TOO_SMALL:

                        // completed synchronously
                        SrvReleaseExecContextAsync(pExecContext);

                        break;

                    default:

                        BAIL_ON_NT_STATUS(ntStatus);

                        break;
                }

                break;

            case STATUS_SUCCESS:

                if (!pNTTransactState->pResponseBuffer)
                {
                    pExecContext->ioStatusBlock.Status = STATUS_BUFFER_TOO_SMALL;
                }
                else
                {
                    pNTTransactState->usActualResponseLen =
                            pExecContext->ioStatusBlock.BytesTransferred;
                    bContinue = FALSE;
                }

                break;

            default:

                BAIL_ON_NT_STATUS(ntStatus);

                break;
        }

    } while (bContinue);

error:

    return ntStatus;
}

static
NTSTATUS
SrvExecuteEnumerateSnapshots(
    PSRV_EXEC_CONTEXT pExecContext
    )
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    PSRV_EXEC_CONTEXT_SMB_V1 pCtxSmb1 = pExecContext->pProtocolContext;
    PSRV_NTTRANSACT_STATE_SMB_V1 pNTTransactState = pCtxSmb1->hState;
    ULONG outputBufferSize = 0;
    PBYTE pSnapshotArray = NULL;

    // There should be no input buffer.
    if (pNTTransactState->pRequestHeader->ulTotalDataCount != 0)
    {
        ntStatus = STATUS_INVALID_PARAMETER;
        BAIL_ON_NT_STATUS(ntStatus);
    }

    if (!pNTTransactState->pResponseBuffer)
    {
        if (pNTTransactState->pRequestHeader->ulMaxDataCount <
                sizeof(SMB_SRV_SNAPSHOT_ARRAY_HEADER))
        {
            ntStatus = STATUS_BUFFER_TOO_SMALL;
            BAIL_ON_NT_STATUS(ntStatus);
        }

        ntStatus = SrvShadowCopyCreateQueryBuffer(
                        pNTTransactState->pRequestHeader->ulMaxDataCount,
                        &pNTTransactState->usResponseBufferLen,
                        OUT_PPVOID(&pNTTransactState->pResponseBuffer));
        BAIL_ON_NT_STATUS(ntStatus);

        SrvPrepareExecContextAsync(pExecContext);

        ntStatus = IoFsControlFile(
                        pNTTransactState->pFile->hFile,
                        pExecContext->pAcb,
                        &pExecContext->ioStatusBlock,
                        IO_FSCTL_QUERY_SNAPSHOTS,
                        NULL,
                        0,
                        pNTTransactState->pResponseBuffer,
                        pNTTransactState->usResponseBufferLen);
        BAIL_ON_NT_STATUS(ntStatus);

        // Completed synchronously
        SrvReleaseExecContextAsync(pExecContext);
    }

    ntStatus = pExecContext->ioStatusBlock.Status;
    BAIL_ON_NT_STATUS(ntStatus);

    // Translate the response buffer that came back from the filesystem to a new
    // buffer to hand back to the client.  The new response buffer will be
    // filled with @GMT token strings instead of LONG64 time stamps.
    ntStatus = SrvShadowCopyCreateEnumerateResponse(
                    pNTTransactState->pRequestHeader->ulMaxDataCount,
                    (PIO_SNAPSHOT_LIST)pNTTransactState->pResponseBuffer,
                    &outputBufferSize,
                    OUT_PPVOID(&pSnapshotArray));
    BAIL_ON_NT_STATUS(ntStatus);

    // Free the response buffer that came from the filesystem and use the new
    // buffer instead.
    SRV_SAFE_FREE_MEMORY_AND_RESET(pNTTransactState->pResponseBuffer);
    pNTTransactState->pResponseBuffer = pSnapshotArray;
    pSnapshotArray = NULL;

    pNTTransactState->usResponseBufferLen = outputBufferSize;
    pNTTransactState->usActualResponseLen = outputBufferSize;

error:
    LWIO_ASSERT(ntStatus != STATUS_BUFFER_OVERFLOW);

    if (ntStatus && (ntStatus != STATUS_PENDING))
    {
        SRV_SAFE_FREE_MEMORY_AND_RESET(pSnapshotArray);
        SRV_SAFE_FREE_MEMORY_AND_RESET(pNTTransactState->pResponseBuffer);
        pNTTransactState->usResponseBufferLen = 0;
        pNTTransactState->usActualResponseLen = 0;
    }

    return ntStatus;
}

static
NTSTATUS
SrvBuildIOCTLResponse(
    PSRV_EXEC_CONTEXT pExecContext
    )
{
    NTSTATUS                     ntStatus     = STATUS_SUCCESS;
    PLWIO_SRV_CONNECTION         pConnection  = pExecContext->pConnection;
    PSRV_EXEC_CONTEXT_SMB_V1     pCtxSmb1     = pExecContext->pProtocolContext;
    PSRV_NTTRANSACT_STATE_SMB_V1 pNTTransactState    = pCtxSmb1->hState;
    ULONG                        iMsg         = pCtxSmb1->iMsg;
    PSRV_MESSAGE_SMB_V1          pSmbRequest  = &pCtxSmb1->pRequests[iMsg];
    PSRV_MESSAGE_SMB_V1          pSmbResponse = &pCtxSmb1->pResponses[iMsg];
    PBYTE pOutBuffer           = pSmbResponse->pBuffer;
    ULONG ulBytesAvailable     = pSmbResponse->ulBytesAvailable;
    ULONG ulOffset             = 0;
    ULONG ulPackageBytesUsed   = 0;
    ULONG ulTotalBytesUsed     = 0;
    UCHAR ucResponseSetupCount = 1;
    ULONG ulDataOffset         = 0;
    ULONG ulParameterOffset    = 0;

    if (!pSmbResponse->ulSerialNum)
    {
        ntStatus = SrvMarshalHeader_SMB_V1(
                        pOutBuffer,
                        ulOffset,
                        ulBytesAvailable,
                        COM_NT_TRANSACT,
                        STATUS_SUCCESS,
                        TRUE,
                        (pSmbRequest->pHeader->flags2 & FLAG2_ERR_STATUS) != 0,
                        pConnection->serverProperties.ulCapabilities,
                        pNTTransactState->pTree->tid,
                        SMB_V1_GET_PROCESS_ID(pSmbRequest->pHeader),
                        pNTTransactState->pSession->uid,
                        pSmbRequest->pHeader->mid,
                        &pSmbResponse->pHeader,
                        &pSmbResponse->pWordCount,
                        &pSmbResponse->pAndXHeader,
                        &pSmbResponse->usHeaderSize);
    }
    else
    {
        ntStatus = SrvMarshalHeaderAndX_SMB_V1(
                        pOutBuffer,
                        ulOffset,
                        ulBytesAvailable,
                        COM_NT_TRANSACT,
                        &pSmbResponse->pWordCount,
                        &pSmbResponse->pAndXHeader,
                        &pSmbResponse->usHeaderSize);
    }
    BAIL_ON_NT_STATUS(ntStatus);

    pOutBuffer       += pSmbResponse->usHeaderSize;
    ulOffset         += pSmbResponse->usHeaderSize;
    ulBytesAvailable -= pSmbResponse->usHeaderSize;
    ulTotalBytesUsed += pSmbResponse->usHeaderSize;

    *pSmbResponse->pWordCount = 18 + ucResponseSetupCount;

    ntStatus = WireMarshallNtTransactionResponse(
                    pOutBuffer,
                    ulBytesAvailable,
                    ulOffset,
                    &pNTTransactState->usActualResponseLen,
                    ucResponseSetupCount,
                    NULL,
                    0,
                    pNTTransactState->pResponseBuffer,
                    pNTTransactState->usActualResponseLen,
                    &ulDataOffset,
                    &ulParameterOffset,
                    &ulPackageBytesUsed);
    BAIL_ON_NT_STATUS(ntStatus);

    // pOutBuffer       += ulPackageBytesUsed;
    // ulOffset         += ulPackageBytesUsed;
    // ulBytesAvailable -= ulPackageBytesUsed;
    ulTotalBytesUsed += ulPackageBytesUsed;

    pSmbResponse->ulMessageSize = ulTotalBytesUsed;

cleanup:

    return ntStatus;

error:

    if (ulTotalBytesUsed)
    {
        pSmbResponse->pHeader = NULL;
        pSmbResponse->pAndXHeader = NULL;
        memset(pSmbResponse->pBuffer, 0, ulTotalBytesUsed);
    }

    pSmbResponse->ulMessageSize = 0;

    goto cleanup;
}

static
NTSTATUS
SrvProcessNotifyChange(
    PSRV_EXEC_CONTEXT pExecContext
    )
{
    NTSTATUS                     ntStatus     = STATUS_SUCCESS;
    PSRV_EXEC_CONTEXT_SMB_V1     pCtxSmb1     = pExecContext->pProtocolContext;
    ULONG                        iMsg         = pCtxSmb1->iMsg;
    PSRV_MESSAGE_SMB_V1          pSmbRequest  = &pCtxSmb1->pRequests[iMsg];
    PSRV_NTTRANSACT_STATE_SMB_V1      pNTTransactState  = pCtxSmb1->hState;
    PLWIO_ASYNC_STATE                 pAsyncState       = NULL;
    PSRV_CHANGE_NOTIFY_STATE_SMB_V1   pNotifyState      = NULL;
    BOOLEAN                           bUnregisterAsync  = FALSE;

    switch (pNTTransactState->stage)
    {
        case SRV_NTTRANSACT_STAGE_SMB_V1_INITIAL:

            if (!pNTTransactState->pRequestHeader->ulMaxParameterCount ||
                (sizeof(SMB_NOTIFY_CHANGE_HEADER) !=
                    pNTTransactState->pRequestHeader->ucSetupCount * sizeof(USHORT)))
            {
                ntStatus = STATUS_INVALID_NETWORK_RESPONSE;
                BAIL_ON_NT_STATUS(ntStatus);
            }

            if (pNTTransactState->pRequestHeader->ulMaxParameterCount >
                    SMB_CN_MAX_BUFFER_SIZE)
            {
                ntStatus = STATUS_INVALID_BUFFER_SIZE;
                BAIL_ON_NT_STATUS(ntStatus);
            }

            pNTTransactState->pNotifyChangeHeader =
                (PSMB_NOTIFY_CHANGE_HEADER)(PBYTE)pNTTransactState->pSetup;

            SrvUnmarshallBoolean(&pNTTransactState->pNotifyChangeHeader->bWatchTree);

            SRV_LOG_DEBUG(
                    pExecContext->pLogContext,
                    SMB_PROTOCOL_VERSION_1,
                    pSmbRequest->pHeader->command,
                    "NT Transact (Change Notify): "
                    "command(%u),uid(%u),mid(%u),pid(%u),tid(%u),"
                    "file-id(%u),completion-filter(0x%x),watch-tree(%s)",
                    pSmbRequest->pHeader->command,
                    pSmbRequest->pHeader->uid,
                    pSmbRequest->pHeader->mid,
                    SMB_V1_GET_PROCESS_ID(pSmbRequest->pHeader),
                    pSmbRequest->pHeader->tid,
                    pNTTransactState->pNotifyChangeHeader->usFid,
                    pNTTransactState->pNotifyChangeHeader->ulCompletionFilter,
                    pNTTransactState->pNotifyChangeHeader->bWatchTree? "TRUE" : "FALSE");

            //
            // Check for cancel after logging but before any work to avoid
            // doing extra work in case the request was cancelled before
            // starting processing.
            //
            if (SrvMpxTrackerIsCancelledExecContext(pExecContext))
            {
                ntStatus = STATUS_CANCELLED;
                BAIL_ON_NT_STATUS(ntStatus);
            }

            ntStatus = SrvTreeFindFile_SMB_V1(
                            pCtxSmb1,
                            pNTTransactState->pTree,
                            pNTTransactState->pNotifyChangeHeader->usFid,
                            &pNTTransactState->pFile);
            BAIL_ON_NT_STATUS(ntStatus);

            ntStatus = SrvNotifyCreateState(
                            pExecContext->pConnection,
                            pCtxSmb1->pSession,
                            pCtxSmb1->pTree,
                            pCtxSmb1->pFile,
                            pSmbRequest->pHeader->flags2,
                            pSmbRequest->pHeader->mid,
                            SMB_V1_GET_PROCESS_ID(pSmbRequest->pHeader),
                            pExecContext->pSmbRequest->sequence,
                            pNTTransactState->pNotifyChangeHeader->ulCompletionFilter,
                            pNTTransactState->pNotifyChangeHeader->bWatchTree,
                            pNTTransactState->pRequestHeader->ulMaxParameterCount,
                            &pNotifyState);
            BAIL_ON_NT_STATUS(ntStatus);

            if (pSmbRequest->pHeader->mid != 0xFFFF)
            {
                SrvMpxTrackerSwapExecContext(
                        pExecContext,
                        pNotifyState->pInternalExecContext);
            }

            ntStatus = SrvAsyncStateCreate(
                            pNotifyState->ullNotifyId,
                            SMB_SUB_COMMAND_NT_TRANSACT_NOTIFY_CHANGE,
                            pNotifyState,
                            &SrvNotifyStateCancel,
                            &SrvNotifyStateReleaseHandle,
                            &pAsyncState);
            BAIL_ON_NT_STATUS(ntStatus);

            SrvNotifyStateAcquire(pNotifyState);

            ntStatus = SrvTreeAddAsyncState(pCtxSmb1->pTree, pAsyncState);
            BAIL_ON_NT_STATUS(ntStatus);

            bUnregisterAsync = TRUE;

            //
            // Check for cancel a final time *after* adding the async state
            // so that a cancel is caught at least here or if async state
            // is cancelled.
            //
            if (SrvMpxTrackerIsCancelledExecContext(pExecContext))
            {
                ntStatus = STATUS_CANCELLED;
                BAIL_ON_NT_STATUS(ntStatus);
            }

            pNTTransactState->stage = SRV_NTTRANSACT_STAGE_SMB_V1_ATTEMPT_IO;

            // intentional fall through

        case SRV_NTTRANSACT_STAGE_SMB_V1_ATTEMPT_IO:

            ntStatus = SrvExecuteChangeNotify(pExecContext, pNotifyState);
            BAIL_ON_NT_STATUS(ntStatus);

            pNTTransactState->stage = SRV_NTTRANSACT_STAGE_SMB_V1_BUILD_RESPONSE;

            // intentional fall through

        case SRV_NTTRANSACT_STAGE_SMB_V1_BUILD_RESPONSE:

            ntStatus = SrvBuildChangeNotifyResponse(pExecContext, pNotifyState);
            BAIL_ON_NT_STATUS(ntStatus);

            pNTTransactState->stage = SRV_NTTRANSACT_STAGE_SMB_V1_DONE;

            // intentional fall through

        case SRV_NTTRANSACT_STAGE_SMB_V1_DONE:

            break;

        case SRV_NTTRANSACT_STAGE_SMB_V1_CREATE_COMPLETED:
        case SRV_NTTRANSACT_STAGE_SMB_V1_QUERY_INFO:
        case SRV_NTTRANSACT_STAGE_SMB_V1_GET_OPLOCKS:

            ntStatus = STATUS_INTERNAL_ERROR;

            break;
    }

cleanup:

    if (pNotifyState)
    {
        if (bUnregisterAsync)
        {
            SrvTreeRemoveAsyncState(pCtxSmb1->pTree, pNotifyState->ullNotifyId);
        }

        SrvNotifyStateRelease(pNotifyState);
    }

    if (pAsyncState)
    {
        SrvAsyncStateRelease(pAsyncState);
    }

    return ntStatus;

error:

    switch (ntStatus)
    {
        case STATUS_PENDING:

            bUnregisterAsync = FALSE;

            break;

        default:

            if (pNotifyState)
            {
                SrvReleaseNotifyStateAsync(pNotifyState);
            }

            break;
    }

    goto cleanup;
}

static
NTSTATUS
SrvExecuteChangeNotify(
    PSRV_EXEC_CONTEXT               pExecContext,
    PSRV_CHANGE_NOTIFY_STATE_SMB_V1 pNotifyState
    )
{
    NTSTATUS                   ntStatus     = STATUS_SUCCESS;
    PSRV_EXEC_CONTEXT_SMB_V1   pCtxSmb1     = pExecContext->pProtocolContext;

    SrvPrepareNotifyStateAsync(pNotifyState);

    ntStatus = IoReadDirectoryChangeFile(
                    pCtxSmb1->pFile->hFile,
                    pNotifyState->pAcb,
                    &pNotifyState->ioStatusBlock,
                    pNotifyState->pBuffer,
                    pNotifyState->ulBufferLength,
                    pNotifyState->bWatchTree,
                    pNotifyState->ulCompletionFilter,
                    &pNotifyState->ulMaxBufferSize);

    if (ntStatus == STATUS_NOT_SUPPORTED)
    {
        //
        // The file system driver does not have support
        // to keep accumulating file change notifications
        //
        ntStatus = IoReadDirectoryChangeFile(
                        pCtxSmb1->pFile->hFile,
                        pNotifyState->pAcb,
                        &pNotifyState->ioStatusBlock,
                        pNotifyState->pBuffer,
                        pNotifyState->ulBufferLength,
                        pNotifyState->bWatchTree,
                        pNotifyState->ulCompletionFilter,
                        NULL);
    }

    if (ntStatus == STATUS_NOTIFY_ENUM_DIR)
    {
        ntStatus = STATUS_SUCCESS;
    }
    else if (ntStatus == STATUS_CANCELLED)
    {
        if (SrvFileIsRundown(pCtxSmb1->pFile))
        {
            ntStatus = STATUS_SUCCESS;
            pNotifyState->ioStatusBlock.Status = STATUS_SUCCESS;
            pNotifyState->ioStatusBlock.BytesTransferred = 0;
        }
    }

    BAIL_ON_NT_STATUS(ntStatus);

    SrvReleaseNotifyStateAsync(pNotifyState); // Completed synchronously

    pNotifyState->ulBytesUsed = pNotifyState->ioStatusBlock.BytesTransferred;

error:

    return ntStatus;
}

static
NTSTATUS
SrvProcessChangeNotifyCompletion(
    PSRV_EXEC_CONTEXT pExecContext
    )
{
    NTSTATUS                   ntStatus     = STATUS_SUCCESS;
    PLWIO_SRV_CONNECTION       pConnection  = pExecContext->pConnection;
    PSRV_EXEC_CONTEXT_SMB_V1   pCtxSmb1     = pExecContext->pProtocolContext;
    ULONG                      iMsg         = pCtxSmb1->iMsg;
    PSRV_MESSAGE_SMB_V1        pSmbRequest  = &pCtxSmb1->pRequests[iMsg];
    PSRV_MESSAGE_SMB_V1        pSmbResponse = &pCtxSmb1->pResponses[iMsg];
    PLWIO_SRV_SESSION          pSession     = NULL;
    PLWIO_SRV_TREE             pTree        = NULL;
    PLWIO_SRV_FILE             pFile        = NULL;
    BOOLEAN                    bInLock      = FALSE;
    PLWIO_ASYNC_STATE          pAsyncState  = NULL;
    ULONG64                    ullNotifyId  = 0LL;
    PSRV_CHANGE_NOTIFY_STATE_SMB_V1 pNotifyState = NULL;

    ntStatus = SrvConnectionFindSession_SMB_V1(
                    pCtxSmb1,
                    pConnection,
                    pSmbRequest->pHeader->uid,
                    &pSession);
    BAIL_ON_NT_STATUS(ntStatus);

    ntStatus = SrvSessionFindTree_SMB_V1(
                    pCtxSmb1,
                    pSession,
                    pSmbRequest->pHeader->tid,
                    &pTree);
    BAIL_ON_NT_STATUS(ntStatus);

    ullNotifyId = SrvAsyncStateBuildId(
                    SMB_V1_GET_PROCESS_ID(pSmbRequest->pHeader),
                    pSmbRequest->pHeader->mid);

    ntStatus = SrvTreeFindAsyncState(pTree, ullNotifyId, &pAsyncState);
    if (ntStatus == STATUS_NOT_FOUND)
    {
        // The request must have been rundown.
        ntStatus = STATUS_CANCELLED;
    }
    BAIL_ON_NT_STATUS(ntStatus);

    SrvTreeRemoveAsyncState(pTree, ullNotifyId);

    pNotifyState = pAsyncState->hAsyncState;

    if (pSmbRequest->pHeader->error == STATUS_CANCELLED)
    {
        ntStatus = SrvTreeFindFile_SMB_V1(
                        pCtxSmb1,
                        pTree,
                        pNotifyState->usFid,
                        &pFile);
        if (ntStatus == STATUS_INVALID_HANDLE)
        {
            ntStatus = STATUS_SUCCESS;
        }
        BAIL_ON_NT_STATUS(ntStatus);

        // If the file is closed, we should return STATUS_SUCCESS instead.
        if (!pFile || SrvFileIsRundown(pFile))
        {
            pSmbRequest->pHeader->error = STATUS_SUCCESS;
            pNotifyState->ioStatusBlock.Status = STATUS_SUCCESS;
            pNotifyState->ioStatusBlock.BytesTransferred = 0;
        }
    }

    LWIO_LOCK_MUTEX(bInLock, &pNotifyState->mutex);

    switch (pSmbRequest->pHeader->error)
    {
        case STATUS_CANCELLED:
        case STATUS_NOTIFY_ENUM_DIR:

            ntStatus = pSmbRequest->pHeader->error;

            break;

        case STATUS_SUCCESS:

            pNotifyState->ulBytesUsed =
                                pNotifyState->ioStatusBlock.BytesTransferred;
            ntStatus = SrvBuildChangeNotifyResponse(pExecContext, pNotifyState);

            break;

        default:

            ntStatus = STATUS_INTERNAL_ERROR;

            break;
    }
    BAIL_ON_NT_STATUS(ntStatus);

cleanup:

    if (pNotifyState)
    {
        LWIO_UNLOCK_MUTEX(bInLock, &pNotifyState->mutex);
    }

    if (pAsyncState)
    {
        SrvAsyncStateRelease(pAsyncState);
    }

    if (pFile)
    {
        SrvFileRelease(pFile);
    }

    if (pTree)
    {
        SrvTreeRelease(pTree);
    }

    if (pSession)
    {
        SrvSessionRelease(pSession);
    }

    return ntStatus;

error:

    // Need to build error response here since exec context is internal.
    ntStatus = SrvBuildErrorResponse_SMB_V1(
                    pConnection,
                    pSmbRequest->pHeader,
                    ntStatus,
                    pSmbResponse);

    goto cleanup;
}

static
NTSTATUS
SrvBuildChangeNotifyResponse(
    PSRV_EXEC_CONTEXT               pExecContext,
    PSRV_CHANGE_NOTIFY_STATE_SMB_V1 pNotifyState
    )
{
    NTSTATUS                     ntStatus     = STATUS_SUCCESS;
    PLWIO_SRV_CONNECTION         pConnection  = pExecContext->pConnection;
    PSRV_EXEC_CONTEXT_SMB_V1     pCtxSmb1     = pExecContext->pProtocolContext;
    ULONG                        iMsg         = pCtxSmb1->iMsg;
    PSRV_MESSAGE_SMB_V1          pSmbRequest  = &pCtxSmb1->pRequests[iMsg];
    PSRV_MESSAGE_SMB_V1          pSmbResponse = &pCtxSmb1->pResponses[iMsg];
    PUSHORT                      pSetup       = NULL;
    PBYTE                        pData        = NULL;
    PBYTE pOutBuffer           = pSmbResponse->pBuffer;
    ULONG ulBytesAvailable     = pSmbResponse->ulBytesAvailable;
    ULONG ulOffset             = 0;
    ULONG ulPackageBytesUsed   = 0;
    ULONG ulTotalBytesUsed     = 0;
    UCHAR ucResponseSetupCount = 0;
    ULONG ulDataLength         = 0;
    ULONG ulDataOffset         = 0;
    ULONG ulParameterOffset    = 0;

    if (!pSmbResponse->ulSerialNum)
    {
        ntStatus = SrvMarshalHeader_SMB_V1(
                        pOutBuffer,
                        ulOffset,
                        ulBytesAvailable,
                        COM_NT_TRANSACT,
                        STATUS_SUCCESS,
                        TRUE,
                        (pSmbRequest->pHeader->flags2 & FLAG2_ERR_STATUS) != 0,
                        pConnection->serverProperties.ulCapabilities,
                        pNotifyState->usTid,
                        pNotifyState->ulPid,
                        pNotifyState->usUid,
                        pNotifyState->usMid,
                        &pSmbResponse->pHeader,
                        &pSmbResponse->pWordCount,
                        &pSmbResponse->pAndXHeader,
                        &pSmbResponse->usHeaderSize);
    }
    else
    {
        ntStatus = SrvMarshalHeaderAndX_SMB_V1(
                        pOutBuffer,
                        ulOffset,
                        ulBytesAvailable,
                        COM_NT_TRANSACT,
                        &pSmbResponse->pWordCount,
                        &pSmbResponse->pAndXHeader,
                        &pSmbResponse->usHeaderSize);
    }
    BAIL_ON_NT_STATUS(ntStatus);

    pOutBuffer       += pSmbResponse->usHeaderSize;
    ulOffset         += pSmbResponse->usHeaderSize;
    ulBytesAvailable -= pSmbResponse->usHeaderSize;
    ulTotalBytesUsed += pSmbResponse->usHeaderSize;

    *pSmbResponse->pWordCount = 18 + ucResponseSetupCount;

    ntStatus = WireMarshallNtTransactionResponse(
                    pOutBuffer,
                    ulBytesAvailable,
                    ulOffset,
                    pSetup,
                    ucResponseSetupCount,
                    pNotifyState->pBuffer,
                    pNotifyState->ulBytesUsed,
                    pData,
                    ulDataLength,
                    &ulDataOffset,
                    &ulParameterOffset,
                    &ulPackageBytesUsed);
    BAIL_ON_NT_STATUS(ntStatus);

    // pOutBuffer       += ulPackageBytesUsed;
    // ulOffset         += ulPackageBytesUsed;
    // ulBytesAvailable -= ulPackageBytesUsed;
    ulTotalBytesUsed += ulPackageBytesUsed;

    pSmbResponse->ulMessageSize = ulTotalBytesUsed;

cleanup:

    return ntStatus;

error:

    if (ulTotalBytesUsed)
    {
        pSmbResponse->pHeader = NULL;
        pSmbResponse->pAndXHeader = NULL;
        memset(pSmbResponse->pBuffer, 0, ulTotalBytesUsed);
    }

    pSmbResponse->ulMessageSize = 0;

    goto cleanup;
}

static
NTSTATUS
SrvProcessNtTransactCreate(
    PSRV_EXEC_CONTEXT pExecContext
    )
{
    NTSTATUS                     ntStatus     = STATUS_SUCCESS;
    PSRV_EXEC_CONTEXT_SMB_V1     pCtxSmb1     = pExecContext->pProtocolContext;
    PSRV_NTTRANSACT_STATE_SMB_V1 pNTTransactState = pCtxSmb1->hState;

    switch (pNTTransactState->stage)
    {
        case SRV_NTTRANSACT_STAGE_SMB_V1_INITIAL:

            ntStatus = SrvParseNtTransactCreateParameters(pExecContext);
            BAIL_ON_NT_STATUS(ntStatus);

            SRV_LOG_CALL_DEBUG(
                    pExecContext->pLogContext,
                    SMB_PROTOCOL_VERSION_1,
                    pCtxSmb1->pRequests[pCtxSmb1->iMsg].pHeader->command,
                    &SrvLogNtTransactCreateState_SMB_V1,
                    pNTTransactState);

            pNTTransactState->stage =
                                SRV_NTTRANSACT_STAGE_SMB_V1_CREATE_COMPLETED;

            SrvPrepareExecContextAsync(pExecContext);

            ntStatus = SrvIoCreateFile(
                            pExecContext,
                            pNTTransactState->pTree->pShareInfo,
                            &pNTTransactState->hFile,
                            pExecContext->pAcb,
                            &pExecContext->ioStatusBlock,
                            pNTTransactState->pSession->pIoSecurityContext,
                            pNTTransactState->pFilename,
                            (PSECURITY_DESCRIPTOR_RELATIVE)pNTTransactState->pSecDesc,
                            pNTTransactState->pSecurityQOS,
                            pNTTransactState->pNtTransactCreateHeader->ulDesiredAccess | FILE_READ_ATTRIBUTES,
                            pNTTransactState->pNtTransactCreateHeader->ullAllocationSize,
                            pNTTransactState->pNtTransactCreateHeader->ulExtFileAttributes,
                            pNTTransactState->pNtTransactCreateHeader->ulShareAccess,
                            pNTTransactState->pNtTransactCreateHeader->ulCreateDisposition,
                            pNTTransactState->pNtTransactCreateHeader->ulCreateOptions,
                            pNTTransactState->pEA,
                            pNTTransactState->pNtTransactCreateHeader->ulEALength,
                            &pNTTransactState->SnapshotTime,
                            &pNTTransactState->pEcpList);
            BAIL_ON_NT_STATUS(ntStatus);

            SrvReleaseExecContextAsync(pExecContext); // completed sync

            // intentional fall through

        case SRV_NTTRANSACT_STAGE_SMB_V1_CREATE_COMPLETED:

            ntStatus = pExecContext->ioStatusBlock.Status;
            BAIL_ON_NT_STATUS(ntStatus);

            pNTTransactState->ulCreateAction =
                            pExecContext->ioStatusBlock.CreateResult;

            ntStatus = SrvTreeCreateFile(
                            pNTTransactState->pTree,
                            pNTTransactState->pwszFilename,
                            &pNTTransactState->hFile,
                            &pNTTransactState->pFilename,
                            pNTTransactState->pNtTransactCreateHeader->ulDesiredAccess,
                            pNTTransactState->pNtTransactCreateHeader->ullAllocationSize,
                            pNTTransactState->pNtTransactCreateHeader->ulExtFileAttributes,
                            pNTTransactState->pNtTransactCreateHeader->ulShareAccess,
                            pNTTransactState->pNtTransactCreateHeader->ulCreateDisposition,
                            pNTTransactState->pNtTransactCreateHeader->ulCreateOptions,
                            pNTTransactState->SnapshotTime,
                            &pNTTransactState->pFile);
            BAIL_ON_NT_STATUS(ntStatus);

            pNTTransactState->pFile->pfnCancelAsyncOperationsFile = SrvCancelFileAsyncOperations;

            pNTTransactState->stage = SRV_NTTRANSACT_STAGE_SMB_V1_QUERY_INFO;

            // intentional fall through

        case SRV_NTTRANSACT_STAGE_SMB_V1_QUERY_INFO:

            ntStatus = SrvQueryNTTransactFileInformation(pExecContext);
            BAIL_ON_NT_STATUS(ntStatus);

            pNTTransactState->stage = SRV_NTTRANSACT_STAGE_SMB_V1_GET_OPLOCKS;

            // intentional fall through

        case SRV_NTTRANSACT_STAGE_SMB_V1_GET_OPLOCKS:

            ntStatus = SrvRequestNTTransactOplocks(pExecContext);
            // We don't fail to if the oplock cannot be granted

            pNTTransactState->stage = SRV_NTTRANSACT_STAGE_SMB_V1_BUILD_RESPONSE;

            // intentional fall through

        case SRV_NTTRANSACT_STAGE_SMB_V1_BUILD_RESPONSE:

            ntStatus = pExecContext->ioStatusBlock.Status;
            BAIL_ON_NT_STATUS(ntStatus);

            if (!pNTTransactState->pFile->hCancellableBRLStateList)
            {
                PSRV_BYTE_RANGE_LOCK_STATE_LIST pBRLStateList = NULL;

                ntStatus = SrvCreatePendingLockStateList(&pBRLStateList);
                BAIL_ON_NT_STATUS(ntStatus);

                pNTTransactState->pFile->hCancellableBRLStateList =
                                (HANDLE)pBRLStateList;

                pNTTransactState->pFile->pfnFreeBRLStateList =
                                &SrvFreePendingLockStateListHandle;
            }

            ntStatus = SrvBuildNTTransactCreateResponse(pExecContext);
            BAIL_ON_NT_STATUS(ntStatus);

            ntStatus = SrvElementsRegisterResource(
                            &pNTTransactState->pFile->resource,
                            NULL);
            BAIL_ON_NT_STATUS(ntStatus);

            pNTTransactState->stage = SRV_NTTRANSACT_STAGE_SMB_V1_DONE;

            // intentional fall through

        case SRV_NTTRANSACT_STAGE_SMB_V1_DONE:

            if (pNTTransactState->pNtTransactCreateHeader->ulDesiredAccess & FILE_READ_DATA)
            {
                pNTTransactState->pFile->ulPermissions |= SRV_PERM_FILE_READ;
            }
            if (pNTTransactState->pNtTransactCreateHeader->ulDesiredAccess & FILE_WRITE_DATA)
            {
                pNTTransactState->pFile->ulPermissions |= SRV_PERM_FILE_WRITE;
            }
            if (pNTTransactState->ulCreateAction == FILE_CREATED)
            {
                pNTTransactState->pFile->ulPermissions |= SRV_PERM_FILE_CREATE;
            }

            // transfer file so we do not run it down
            pCtxSmb1->pFile = pNTTransactState->pFile;
            pNTTransactState->pFile = NULL;

            break;

        case SRV_NTTRANSACT_STAGE_SMB_V1_ATTEMPT_IO:

            ntStatus = STATUS_INTERNAL_ERROR;

            break;
    }

cleanup:

    return ntStatus;

error:

    goto cleanup;
}

static
NTSTATUS
SrvParseNtTransactCreateParameters(
    PSRV_EXEC_CONTEXT pExecContext
    )
{
    NTSTATUS                     ntStatus     = STATUS_SUCCESS;
    PLWIO_SRV_CONNECTION         pConnection  = pExecContext->pConnection;
    PSRV_EXEC_CONTEXT_SMB_V1     pCtxSmb1     = pExecContext->pProtocolContext;
    ULONG                        iMsg         = pCtxSmb1->iMsg;
    PSRV_MESSAGE_SMB_V1          pSmbRequest  = &pCtxSmb1->pRequests[iMsg];
    PSRV_NTTRANSACT_STATE_SMB_V1 pNTTransactState = pCtxSmb1->hState;
    PBYTE                        pDataCursor      = NULL;
    ULONG                        ulOffset         = 0;
    ULONG                        ulBytesAvailable = 0;
    PWSTR                        pwszFilename     = NULL; // do not free
    BOOLEAN                      bShareInLock      = FALSE;

    pDataCursor      = pNTTransactState->pParameters;
    ulOffset         = pNTTransactState->pRequestHeader->ulParameterOffset;
    ulBytesAvailable = pNTTransactState->pRequestHeader->ulParameterCount;

    if (ulBytesAvailable < sizeof(SMB_NT_TRANSACT_CREATE_REQUEST_HEADER))
    {
        ntStatus = STATUS_INVALID_NETWORK_RESPONSE;
        BAIL_ON_NT_STATUS(ntStatus);
    }

    pNTTransactState->pNtTransactCreateHeader =
                            (PSMB_NT_TRANSACT_CREATE_REQUEST_HEADER)pDataCursor;

    pDataCursor      += sizeof(SMB_NT_TRANSACT_CREATE_REQUEST_HEADER);
    ulOffset         += sizeof(SMB_NT_TRANSACT_CREATE_REQUEST_HEADER);
    ulBytesAvailable -= sizeof(SMB_NT_TRANSACT_CREATE_REQUEST_HEADER);

    if (!pNTTransactState->pNtTransactCreateHeader->ulNameLength)
    {
        ntStatus = STATUS_INVALID_NETWORK_RESPONSE;
        BAIL_ON_NT_STATUS(ntStatus);
    }

    if (ulOffset % 2)
    {
        if (ulBytesAvailable < 1)
        {
            ntStatus = STATUS_INVALID_NETWORK_RESPONSE;
            BAIL_ON_NT_STATUS(ntStatus);
        }

        pDataCursor++;
        ulOffset++;
        ulBytesAvailable--;
    }

    if (ulBytesAvailable <
            pNTTransactState->pNtTransactCreateHeader->ulNameLength)
    {
        ntStatus = STATUS_INVALID_NETWORK_RESPONSE;
        BAIL_ON_NT_STATUS(ntStatus);
    }

    ntStatus = SrvAllocateMemory(
            pNTTransactState->pNtTransactCreateHeader->ulNameLength + sizeof(wchar16_t),
            OUT_PPVOID(&pNTTransactState->pwszFilename));
    BAIL_ON_NT_STATUS(ntStatus);

    memcpy( (PBYTE)pNTTransactState->pwszFilename,
            pDataCursor,
            pNTTransactState->pNtTransactCreateHeader->ulNameLength);

    pwszFilename = pNTTransactState->pwszFilename;

    pDataCursor      = pNTTransactState->pData;
    ulOffset         = pNTTransactState->pRequestHeader->ulDataOffset;
    ulBytesAvailable = pNTTransactState->pRequestHeader->ulDataCount;

    if (pNTTransactState->pNtTransactCreateHeader->ulSecurityDescLength)
    {
        SECURITY_INFORMATION secInfoAll = 0;

        if (ulBytesAvailable <
                pNTTransactState->pNtTransactCreateHeader->ulSecurityDescLength)
        {
            ntStatus = STATUS_INVALID_NETWORK_RESPONSE;
            BAIL_ON_NT_STATUS(ntStatus);
        }

        pNTTransactState->pSecDesc = pDataCursor;

        if (!RtlValidRelativeSecurityDescriptor(
                        (PSECURITY_DESCRIPTOR_RELATIVE)pNTTransactState->pSecDesc,
                        pNTTransactState->pNtTransactCreateHeader->ulSecurityDescLength,
                        secInfoAll))
        {
            ntStatus = STATUS_INVALID_NETWORK_RESPONSE;
            BAIL_ON_NT_STATUS(ntStatus);
        }

        pDataCursor      +=
                pNTTransactState->pNtTransactCreateHeader->ulSecurityDescLength;
        ulOffset         +=
                pNTTransactState->pNtTransactCreateHeader->ulSecurityDescLength;
        ulBytesAvailable -=
                pNTTransactState->pNtTransactCreateHeader->ulSecurityDescLength;
    }

    if (pNTTransactState->pNtTransactCreateHeader->ulEALength)
    {
        if (ulBytesAvailable <
                pNTTransactState->pNtTransactCreateHeader->ulEALength)
        {
            ntStatus = STATUS_INVALID_NETWORK_RESPONSE;
            BAIL_ON_NT_STATUS(ntStatus);
        }

        pNTTransactState->pEA = pDataCursor;

        // pDataCursor      +=
        //         pNTTransactState->pNtTransactCreateHeader->ulEALength;
        // ulOffset         +=
        //         pNTTransactState->pNtTransactCreateHeader->ulEALength;
        // ulBytesAvailable -=
        //         pNTTransactState->pNtTransactCreateHeader->ulEALength;
    }

    ntStatus = SrvAllocateMemory(
                    sizeof(IO_FILE_NAME),
                    OUT_PPVOID(&pNTTransactState->pFilename));
    BAIL_ON_NT_STATUS(ntStatus);

    if (SrvShouldProcessSnapshots(pSmbRequest->pHeader,
                                  pNTTransactState->pTree->UnderstandSnapshots))
    {
        ntStatus = WirePathParseOutSnapshotTime(
                        pwszFilename,
                        &pNTTransactState->SnapshotTime);
        BAIL_ON_NT_STATUS(ntStatus);
    }

    if (pNTTransactState->pNtTransactCreateHeader->ulRootDirectoryFid != 0)
    {
        wchar16_t wszFwdSlash[]  = {'/',  0};
        wchar16_t wszBackSlash[] = {'\\', 0};

        ntStatus = SrvTreeFindFile(
                        pNTTransactState->pTree,
                        pNTTransactState->pNtTransactCreateHeader->ulRootDirectoryFid,
                        &pNTTransactState->pRootDirectory);
        BAIL_ON_NT_STATUS(ntStatus);

        if (pwszFilename && *pwszFilename)
        {
            if ((*pwszFilename == wszFwdSlash[0]) ||
                (*pwszFilename == wszBackSlash[0]))
            {
                ntStatus = STATUS_INVALID_PARAMETER;
                BAIL_ON_NT_STATUS(ntStatus);
            }

            LWIO_LOCK_RWMUTEX_SHARED(bShareInLock, &pNTTransactState->pTree->mutex);

            ntStatus = SrvBuildFilePath(
                            NULL, /* relative path */
                            pwszFilename,
                            &pNTTransactState->pFilename->Name);
            BAIL_ON_NT_STATUS(ntStatus);

            LWIO_UNLOCK_RWMUTEX(bShareInLock, &pNTTransactState->pTree->mutex);
        }

        pNTTransactState->pFilename->RootFileHandle =
                        pNTTransactState->pRootDirectory->hFile;
    }
    else
    {
        ntStatus = SrvBuildTreeRelativePath(
                        pNTTransactState->pTree,
                        pwszFilename,
                        pNTTransactState->pFilename);
        BAIL_ON_NT_STATUS(ntStatus);
    }

    ntStatus = IoRtlEcpListAllocate(&pNTTransactState->pEcpList);
    BAIL_ON_NT_STATUS(ntStatus);

    ntStatus = IoRtlEcpListInsert(pNTTransactState->pEcpList,
                                  IO_ECP_TYPE_NET_OPEN_INFO,
                                  &pNTTransactState->networkOpenInfo,
                                  sizeof(pNTTransactState->networkOpenInfo),
                                  NULL);
    BAIL_ON_NT_STATUS(ntStatus);

    /* For named pipes, we need to pipe some extra data into the npfs driver:
     *  - Session key
     *  - Client principal name
     *  - Client address
     */
    if (SrvTreeIsNamedPipe(pNTTransactState->pTree))
    {
        ntStatus = SrvSessionGetNamedPipeSessionKey(
                       pCtxSmb1->pSession,
                       pNTTransactState->pEcpList);
        BAIL_ON_NT_STATUS(ntStatus);

        ntStatus = SrvConnectionGetNamedPipeClientAddress(
                       pConnection,
                       pNTTransactState->pEcpList);
        BAIL_ON_NT_STATUS(ntStatus);

        ntStatus = IoRtlEcpListInsert(pNTTransactState->pEcpList,
                                      IO_ECP_TYPE_PIPE_INFO,
                                      &pNTTransactState->filePipeInfo,
                                      sizeof(pNTTransactState->filePipeInfo),
                                      NULL);
        BAIL_ON_NT_STATUS(ntStatus);

        ntStatus = IoRtlEcpListInsert(pNTTransactState->pEcpList,
                                      IO_ECP_TYPE_PIPE_LOCAL_INFO,
                                      &pNTTransactState->filePipeLocalInfo,
                                      sizeof(pNTTransactState->filePipeLocalInfo),
                                      NULL);
        BAIL_ON_NT_STATUS(ntStatus);
    }

cleanup:

    LWIO_UNLOCK_RWMUTEX(
            bShareInLock,
            &pNTTransactState->pTree->pShareInfo->Mutex);

    return ntStatus;

error:

    goto cleanup;
}

static
VOID
SrvLogNtTransactCreateState_SMB_V1(
    PSRV_LOG_CONTEXT pLogContext,
    LWIO_LOG_LEVEL   logLevel,
    PCSTR            pszFunction,
    PCSTR            pszFile,
    ULONG            ulLine,
    ...
    )
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    PSRV_NTTRANSACT_STATE_SMB_V1 pCreateState = NULL;
    PSTR pszPath = NULL;
    va_list msgList;

    va_start(msgList, ulLine);

    pCreateState = va_arg(msgList, PSRV_NTTRANSACT_STATE_SMB_V1);

    if (pCreateState)
    {
        ntStatus = SrvUnicodeStringToMbs(
                        &pCreateState->pFilename->Name,
                        &pszPath);
        BAIL_ON_NT_STATUS(ntStatus);

        LW_RTL_LOG_RAW(
            logLevel,
            "srv",
            pszFunction,
            pszFile,
            ulLine,
            "NtTransact Create state: "
            "alloc-size(%llu),create-disp(0x%x),desired-access(0x%x),"
            "create-options(0x%x),share-access(0x%x),flags(0x%x),"
            "sec-flags(0x%x),root-dir-fid(%u),ea-length(%u),sec-desc-length(%u),path(%s)",
            pCreateState->pNtTransactCreateHeader->ullAllocationSize,
            pCreateState->pNtTransactCreateHeader->ulCreateDisposition,
            pCreateState->pNtTransactCreateHeader->ulDesiredAccess,
            pCreateState->pNtTransactCreateHeader->ulCreateOptions,
            pCreateState->pNtTransactCreateHeader->ullAllocationSize,
            pCreateState->pNtTransactCreateHeader->ulFlags,
            pCreateState->pNtTransactCreateHeader->ucSecurityFlags,
            pCreateState->pNtTransactCreateHeader->ulRootDirectoryFid,
            pCreateState->pNtTransactCreateHeader->ulEALength,
            pCreateState->pNtTransactCreateHeader->ulSecurityDescLength,
            LWIO_SAFE_LOG_STRING(pszPath));
    }

error:

    va_end(msgList);

    SRV_SAFE_FREE_MEMORY(pszPath);

    return;
}

static
NTSTATUS
SrvQueryNTTransactFileInformation(
    PSRV_EXEC_CONTEXT pExecContext
    )
{
    NTSTATUS                     ntStatus = 0;
    PSRV_EXEC_CONTEXT_SMB_V1     pCtxSmb1     = pExecContext->pProtocolContext;
    PSRV_NTTRANSACT_STATE_SMB_V1 pNTTransactState = pCtxSmb1->hState;

    if (!IoRtlEcpListIsAcknowledged(
            pNTTransactState->pEcpList,
            IO_ECP_TYPE_NET_OPEN_INFO))
    {
        if (!pNTTransactState->pNetworkOpenInfo)
        {
            pNTTransactState->pNetworkOpenInfo = &pNTTransactState->networkOpenInfo;

            SrvPrepareExecContextAsync(pExecContext);

            ntStatus = IoQueryInformationFile(
                            pNTTransactState->pFile->hFile,
                            pExecContext->pAcb,
                            &pExecContext->ioStatusBlock,
                            pNTTransactState->pNetworkOpenInfo,
                            sizeof(pNTTransactState->networkOpenInfo),
                            FileNetworkOpenInformation);
            BAIL_ON_NT_STATUS(ntStatus);

            SrvReleaseExecContextAsync(pExecContext); // completed sync
        }
    }
    else
    {
        pNTTransactState->pNetworkOpenInfo = &pNTTransactState->networkOpenInfo;
    }

    if (!(pNTTransactState->pNetworkOpenInfo->FileAttributes & FILE_ATTRIBUTE_DIRECTORY))
    {
        SrvFileBlockIdleTimeout(pNTTransactState->pFile);
    }
    else
    {
        SrvFileUnblockIdleTimeout(pNTTransactState->pFile);
    }

    if (SrvTreeIsNamedPipe(pNTTransactState->pTree))
    {
        if (!IoRtlEcpListIsAcknowledged(
                pNTTransactState->pEcpList,
                IO_ECP_TYPE_PIPE_INFO))
        {
            if (!pNTTransactState->pFilePipeInfo)
            {
                pNTTransactState->pFilePipeInfo = &pNTTransactState->filePipeInfo;

                SrvPrepareExecContextAsync(pExecContext);

                ntStatus = IoQueryInformationFile(
                                pNTTransactState->pFile->hFile,
                                pExecContext->pAcb,
                                &pExecContext->ioStatusBlock,
                                pNTTransactState->pFilePipeInfo,
                                sizeof(pNTTransactState->filePipeInfo),
                                FilePipeInformation);
                BAIL_ON_NT_STATUS(ntStatus);

                SrvReleaseExecContextAsync(pExecContext); // completed sync
            }
        }
        else
        {
            pNTTransactState->pFilePipeInfo = &pNTTransactState->filePipeInfo;
        }

        if (!IoRtlEcpListIsAcknowledged(
                pNTTransactState->pEcpList,
                IO_ECP_TYPE_PIPE_LOCAL_INFO))
        {
            if (!pNTTransactState->pFilePipeLocalInfo)
            {
                pNTTransactState->pFilePipeLocalInfo = &pNTTransactState->filePipeLocalInfo;

                SrvPrepareExecContextAsync(pExecContext);

                ntStatus = IoQueryInformationFile(
                                pNTTransactState->pFile->hFile,
                                pExecContext->pAcb,
                                &pExecContext->ioStatusBlock,
                                pNTTransactState->pFilePipeLocalInfo,
                                sizeof(pNTTransactState->filePipeLocalInfo),
                                FilePipeLocalInformation);
                BAIL_ON_NT_STATUS(ntStatus);

                SrvReleaseExecContextAsync(pExecContext); // completed sync
            }
        }
        else
        {
            pNTTransactState->pFilePipeLocalInfo = &pNTTransactState->filePipeLocalInfo;
        }
    }

error:

    return ntStatus;
}

static
NTSTATUS
SrvRequestNTTransactOplocks(
    PSRV_EXEC_CONTEXT pExecContext
    )
{
    NTSTATUS        ntStatus           = STATUS_SUCCESS;
    SRV_OPLOCK_INFO batchOplockChain[] =
            {
               { IO_OPLOCK_REQUEST_OPLOCK_BATCH,   SMB_OPLOCK_LEVEL_BATCH },
               { IO_OPLOCK_REQUEST_OPLOCK_LEVEL_2, SMB_OPLOCK_LEVEL_II    },
               { SMB_OPLOCK_LEVEL_NONE,            SMB_OPLOCK_LEVEL_NONE  }
            };
    SRV_OPLOCK_INFO exclOplockChain[] =
            {
               { IO_OPLOCK_REQUEST_OPLOCK_LEVEL_1, SMB_OPLOCK_LEVEL_I     },
               { IO_OPLOCK_REQUEST_OPLOCK_LEVEL_2, SMB_OPLOCK_LEVEL_II    },
               { SMB_OPLOCK_LEVEL_NONE,            SMB_OPLOCK_LEVEL_NONE  }
            };
    SRV_OPLOCK_INFO noOplockChain[] =
            {
               { SMB_OPLOCK_LEVEL_NONE,            SMB_OPLOCK_LEVEL_NONE  }
            };
    PSRV_OPLOCK_INFO           pOplockCursor = NULL;
    PSRV_EXEC_CONTEXT_SMB_V1     pCtxSmb1      = pExecContext->pProtocolContext;
    PSRV_NTTRANSACT_STATE_SMB_V1 pNTTransactState = pCtxSmb1->hState;
    PSRV_OPLOCK_STATE_SMB_V1     pOplockState  = NULL;
    BOOLEAN                      bContinue     = TRUE;

    if (SrvTreeIsNamedPipe(pNTTransactState->pTree) ||
        (pNTTransactState->pNetworkOpenInfo->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) ||
        (!(pNTTransactState->pNtTransactCreateHeader->ulFlags &
          (SMB_OPLOCK_REQUEST_BATCH|SMB_OPLOCK_REQUEST_EXCLUSIVE))))
    {
        pOplockCursor = &noOplockChain[0];

        goto done;
    }

    ntStatus = SrvBuildOplockState(
                    pExecContext->pConnection,
                    pNTTransactState->pSession,
                    pNTTransactState->pTree,
                    pNTTransactState->pFile,
                    pExecContext->pSmbRequest->pSMBHeader->flags2,
                    &pOplockState);
    BAIL_ON_NT_STATUS(ntStatus);

    if (pNTTransactState->pNtTransactCreateHeader->ulFlags &
                    SMB_OPLOCK_REQUEST_BATCH)
    {
        pOplockCursor = &batchOplockChain[0];
    }
    else if (pNTTransactState->pNtTransactCreateHeader->ulFlags &
                    SMB_OPLOCK_REQUEST_EXCLUSIVE)
    {
        pOplockCursor = &exclOplockChain[0];
    }
    else
    {
        /* We should never get here, since we don't want to uselessly
         * allocate an OplockState; this is handled at the top of the
         * function.
         */
        LWIO_ASSERT(0);
    }

    while (bContinue && (pOplockCursor->oplockRequest != SMB_OPLOCK_LEVEL_NONE))
    {
        pOplockState->oplockBuffer_in.OplockRequestType =
                        pOplockCursor->oplockRequest;

        SrvPrepareOplockStateAsync(pOplockState);

        ntStatus = IoFsControlFile(
                        pNTTransactState->pFile->hFile,
                        pOplockState->pAcb,
                        &pOplockState->ioStatusBlock,
                        IO_FSCTL_OPLOCK_REQUEST,
                        &pOplockState->oplockBuffer_in,
                        sizeof(pOplockState->oplockBuffer_in),
                        &pOplockState->oplockBuffer_out,
                        sizeof(pOplockState->oplockBuffer_out));
        switch (ntStatus)
        {
            case STATUS_OPLOCK_NOT_GRANTED:

                SrvReleaseOplockStateAsync(pOplockState); // completed sync

                pOplockCursor++;

                break;

            case STATUS_PENDING:

                SrvAcquireOplock(pOplockState);

                ntStatus = SrvFileSetOplock(
                                pNTTransactState->pFile,
                                &pOplockState->Oplock);
                if (ntStatus != STATUS_SUCCESS)
                {
                    SrvReleaseOplock(pOplockState);
                }
                BAIL_ON_NT_STATUS(ntStatus);

                SrvFileSetOplockLevel(
                                pNTTransactState->pFile,
                                pOplockCursor->oplockLevel);

                ntStatus = STATUS_SUCCESS;

                bContinue = FALSE;

                break;

            default:

                SrvReleaseOplockStateAsync(pOplockState); // completed sync

                BAIL_ON_NT_STATUS(ntStatus);

                break;
        }
    }

done:

    pNTTransactState->ucOplockLevel = pOplockCursor->oplockLevel;

cleanup:

    if (pOplockState)
    {
        SrvReleaseOplock(pOplockState);
    }

    return ntStatus;

error:

    goto cleanup;
}

static
NTSTATUS
SrvBuildNTTransactCreateResponse(
    PSRV_EXEC_CONTEXT pExecContext
    )
{
    NTSTATUS                   ntStatus     = STATUS_SUCCESS;
    PLWIO_SRV_CONNECTION       pConnection  = pExecContext->pConnection;
    PSRV_EXEC_CONTEXT_SMB_V1   pCtxSmb1     = pExecContext->pProtocolContext;
    ULONG                      iMsg         = pCtxSmb1->iMsg;
    PSRV_MESSAGE_SMB_V1        pSmbRequest  = &pCtxSmb1->pRequests[iMsg];
    PSRV_MESSAGE_SMB_V1        pSmbResponse = &pCtxSmb1->pResponses[iMsg];
    PBYTE   pOutBuffer           = pSmbResponse->pBuffer;
    ULONG   ulBytesAvailable     = pSmbResponse->ulBytesAvailable;
    ULONG   ulOffset             = 0;
    ULONG   ulTotalBytesUsed     = 0;
    ULONG   ulDataOffset         = 0;
    ULONG   ulParameterOffset    = 0;
    ULONG   ulPackageBytesUsed   = 0;
    PUSHORT pSetup               = NULL;
    UCHAR   ucResponseSetupCount = 0;
    PBYTE   pData                = NULL;
    ULONG   ulDataLength         = 0;
    SMB_NT_TRANSACT_CREATE_RESPONSE_HEADER responseHeader   = {0};
    PSRV_NTTRANSACT_STATE_SMB_V1           pNTTransactState = pCtxSmb1->hState;

    if (!pSmbResponse->ulSerialNum)
    {
        ntStatus = SrvMarshalHeader_SMB_V1(
                        pOutBuffer,
                        ulOffset,
                        ulBytesAvailable,
                        COM_NT_TRANSACT,
                        STATUS_SUCCESS,
                        TRUE,
                        (pSmbRequest->pHeader->flags2 & FLAG2_ERR_STATUS) != 0,
                        pConnection->serverProperties.ulCapabilities,
                        pCtxSmb1->pTree->tid,
                        SMB_V1_GET_PROCESS_ID(pSmbRequest->pHeader),
                        pNTTransactState->pSession->uid,
                        pSmbRequest->pHeader->mid,
                        &pSmbResponse->pHeader,
                        &pSmbResponse->pWordCount,
                        &pSmbResponse->pAndXHeader,
                        &pSmbResponse->usHeaderSize);
    }
    else
    {
        ntStatus = SrvMarshalHeaderAndX_SMB_V1(
                        pOutBuffer,
                        ulOffset,
                        ulBytesAvailable,
                        COM_NT_TRANSACT,
                        &pSmbResponse->pWordCount,
                        &pSmbResponse->pAndXHeader,
                        &pSmbResponse->usHeaderSize);
    }
    BAIL_ON_NT_STATUS(ntStatus);

    pOutBuffer       += pSmbResponse->usHeaderSize;
    ulOffset         += pSmbResponse->usHeaderSize;
    ulBytesAvailable -= pSmbResponse->usHeaderSize;
    ulTotalBytesUsed += pSmbResponse->usHeaderSize;

    *pSmbResponse->pWordCount = 18 + ucResponseSetupCount;

    responseHeader.ucOplockLevel     = pNTTransactState->ucOplockLevel;
    responseHeader.usFid             = pNTTransactState->pFile->fid;
    responseHeader.ulCreateAction    = pNTTransactState->ulCreateAction;
    responseHeader.llCreationTime    = pNTTransactState->networkOpenInfo.CreationTime;
    responseHeader.llLastAccessTime  = pNTTransactState->networkOpenInfo.LastAccessTime;
    responseHeader.llLastWriteTime   = pNTTransactState->networkOpenInfo.LastWriteTime;
    responseHeader.llChangeTime      = pNTTransactState->networkOpenInfo.ChangeTime;
    responseHeader.ulExtFileAttributes = pNTTransactState->networkOpenInfo.FileAttributes;
    responseHeader.ullAllocationSize   = pNTTransactState->networkOpenInfo.AllocationSize;
    responseHeader.ullEndOfFile        = pNTTransactState->networkOpenInfo.EndOfFile;

    if (SrvTreeIsNamedPipe(pNTTransactState->pTree))
    {
        ntStatus = SrvMarshallPipeInfo(
                        &pNTTransactState->filePipeInfo,
                        &pNTTransactState->filePipeLocalInfo,
                        &responseHeader.usDeviceState);
        BAIL_ON_NT_STATUS(ntStatus);

        responseHeader.usFileType =
                            (USHORT)pNTTransactState->filePipeInfo.ReadMode;
    }
    else
    {
        responseHeader.usFileType = 0;
        // TODO: Get these values from the driver
        responseHeader.usDeviceState = SMB_DEVICE_STATE_NO_EAS |
                                       SMB_DEVICE_STATE_NO_SUBSTREAMS |
                                       SMB_DEVICE_STATE_NO_REPARSE_TAG;
    }

    responseHeader.bDirectory =
        (pNTTransactState->pNetworkOpenInfo->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? TRUE : FALSE;

    ntStatus = WireMarshallNtTransactionResponse(
                        pOutBuffer,
                        ulBytesAvailable,
                        ulOffset,
                        pSetup,
                        ucResponseSetupCount,
                        (PBYTE)&responseHeader,
                        sizeof(responseHeader),
                        pData,
                        ulDataLength,
                        &ulDataOffset,
                        &ulParameterOffset,
                        &ulPackageBytesUsed);
    BAIL_ON_NT_STATUS(ntStatus);

    // pOutBuffer       += ulPackageBytesUsed;
    // ulOffset         += ulPackageBytesUsed;
    // ulBytesAvailable -= ulPackageBytesUsed;
    ulTotalBytesUsed += ulPackageBytesUsed;

    pSmbResponse->ulMessageSize = ulTotalBytesUsed;

cleanup:

    return ntStatus;

error:

    if (ulTotalBytesUsed)
    {
        pSmbResponse->pHeader = NULL;
        pSmbResponse->pAndXHeader = NULL;
        memset(pSmbResponse->pBuffer, 0, ulTotalBytesUsed);
    }

    pSmbResponse->ulMessageSize = 0;

    goto cleanup;
}

static
NTSTATUS
SrvBuildNTTransactState(
    PNT_TRANSACTION_REQUEST_HEADER pRequestHeader,
    PUSHORT                        pusBytecount,
    PUSHORT                        pSetup,
    PBYTE                          pParameters,
    PBYTE                          pData,
    PSRV_NTTRANSACT_STATE_SMB_V1*  ppNTTransactState
    )
{
    NTSTATUS                     ntStatus         = STATUS_SUCCESS;
    PSRV_NTTRANSACT_STATE_SMB_V1 pNTTransactState = NULL;

    ntStatus = SrvAllocateMemory(
                    sizeof(SRV_NTTRANSACT_STATE_SMB_V1),
                    OUT_PPVOID(&pNTTransactState));
    BAIL_ON_NT_STATUS(ntStatus);

    pNTTransactState->stage = SRV_NTTRANSACT_STAGE_SMB_V1_INITIAL;

    pNTTransactState->pRequestHeader = pRequestHeader;
    pNTTransactState->pusBytecount   = pusBytecount;
    pNTTransactState->pSetup         = pSetup;
    pNTTransactState->pParameters    = pParameters;
    pNTTransactState->pData          = pData;

    *ppNTTransactState = pNTTransactState;

cleanup:

    return ntStatus;

error:

    *ppNTTransactState = NULL;

    if (pNTTransactState)
    {
        SrvFreeNTTransactState(pNTTransactState);
    }

    goto cleanup;
}

static
VOID
SrvFreeNTTransactStateHandle(
    HANDLE hNTTransactState
    )
{
    SrvFreeNTTransactState(hNTTransactState);
}

static
VOID
SrvFreeNTTransactState(
    PSRV_NTTRANSACT_STATE_SMB_V1 pNTTransactState
    )
{
    if (pNTTransactState->pEcpList)
    {
        IoRtlEcpListFree(&pNTTransactState->pEcpList);
    }

    if (pNTTransactState->pFilename)
    {
        SRV_FREE_UNICODE_STRING(&pNTTransactState->pFilename->Name);
        SrvFreeMemory(pNTTransactState->pFilename);
    }

    if (pNTTransactState->pwszFilename)
    {
        SrvFreeMemory(pNTTransactState->pwszFilename);
    }

    // TODO: Free the following if set
    // pSecurityQOS;

    if (pNTTransactState->hFile)
    {
        IoCloseFile(pNTTransactState->hFile);
    }

    if (pNTTransactState->pFile)
    {
        if (pNTTransactState->pRequestHeader->usFunction == SMB_SUB_COMMAND_NT_TRANSACT_CREATE)
        {
            // left over from create, so run it down
            SrvFileRundown(pNTTransactState->pFile);
        }
        SrvFileRelease(pNTTransactState->pFile);
    }

    if (pNTTransactState->pRootDirectory)
    {
        SrvFileRelease(pNTTransactState->pRootDirectory);
    }

    if (pNTTransactState->pTree)
    {
        SrvTreeRelease(pNTTransactState->pTree);
    }

    if (pNTTransactState->pSession)
    {
        SrvSessionRelease(pNTTransactState->pSession);
    }

    if (pNTTransactState->pSecurityDescriptor2)
    {
        SrvFreeMemory(pNTTransactState->pSecurityDescriptor2);
    }

    if (pNTTransactState->pResponseBuffer)
    {
        SrvFreeMemory(pNTTransactState->pResponseBuffer);
    }

    SrvFreeMemory(pNTTransactState);
}
