/* -*- 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
 */

/*
 * Copyright (C) Likewise Software. All rights reserved.
 *
 * Module Name:
 *
 *        treeconnect.c
 *
 * Abstract:
 *
 *        Likewise IO (LWIO) - SRV
 *
 *        Protocols API - SMBV2
 *
 *        Tree Connect
 *
 * Authors: Krishna Ganugapati (kganugapati@likewise.com)
 *          Sriram Nambakam (snambakam@likewise.com)
 *
 *
 */
#include "includes.h"

static
NTSTATUS
SrvBuildTreeConnectState_SMB_V2(
    PLWIO_SRV_CONNECTION              pConnection,
    PLWIO_SRV_SESSION_2               pSession,
    PSMB2_TREE_CONNECT_REQUEST_HEADER pTreeConnectHeader,
    PUNICODE_STRING                   pPath,
    PSRV_TREE_CONNECT_STATE_SMB_V2*   ppTConState
    );

static
NTSTATUS
SrvCreateTreeRootHandle_SMB_V2(
    PSRV_EXEC_CONTEXT pExecContext
    );

static
NTSTATUS
SrvIoPrepareEcpList_SMB_V2(
    IN PLWIO_SRV_SESSION_2 pSession,
    IN PLWIO_SRV_TREE_2 pTree,
    IN PWSTR pwszShareName,
    IN OUT PIO_ECP_LIST* ppEcpList
    );

static
VOID
SrvIoFreeEcpString_SMB_V2(
    IN PVOID pContext
    );

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

static
NTSTATUS
SrvBuildTreeConnectResponse_SMB_V2(
    PSRV_EXEC_CONTEXT pExecContext
    );

static
VOID
SrvFreeTreeConnectStateHandle_SMB_V2(
    HANDLE hTConState
    );

static
VOID
SrvFreeTreeConnectState_SMB_V2(
    PSRV_TREE_CONNECT_STATE_SMB_V2 pTConState
    );

static
VOID
SrvLogTreeConnect2Failure(
    PSRV_EXEC_CONTEXT pExecContext,
    NTSTATUS Status
    )
{
    PSRV_EXEC_CONTEXT_SMB_V2       pCtxSmb2   = pExecContext->pProtocolContext;
    PSRV_TREE_CONNECT_STATE_SMB_V2 pTConState = pCtxSmb2->hState;

    if (pTConState)
    {
        SrvEventTreeConnectFailure(
            pExecContext->pConnection->pClientAddress,
            pCtxSmb2->pSession->pNetbiosDomain,
            pCtxSmb2->pSession->pSamAccountName,
            (pCtxSmb2->pSession->ulUserFlags & SMB_SESSION_FLAG_GUEST)
                ? TRUE : FALSE,
            pTConState->pwszPath,
            Status);
    }
}

NTSTATUS
SrvProcessTreeConnect_SMB_V2(
    PSRV_EXEC_CONTEXT pExecContext
    )
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    PLWIO_SRV_CONNECTION pConnection = pExecContext->pConnection;
    PSRV_EXEC_CONTEXT_SMB_V2       pCtxSmb2   = pExecContext->pProtocolContext;
    PSRV_TREE_CONNECT_STATE_SMB_V2 pTConState = NULL;
    PLWIO_SRV_SESSION_2 pSession      = NULL;
    PWSTR               pwszSharename = NULL;
    PSRV_SHARE_INFO     pShareInfo    = NULL;

    pTConState = pCtxSmb2->hState;
    if (pTConState == NULL)
    {
        ULONG                      iMsg          = pCtxSmb2->iMsg;
        PSRV_MESSAGE_SMB_V2        pSmbRequest   = &pCtxSmb2->pRequests[iMsg];
        PSMB2_TREE_CONNECT_REQUEST_HEADER pTreeConnectHeader = NULL;// Do not free
        UNICODE_STRING    wszPath = {0}; // Do not free

        if (pCtxSmb2->pTree)
        {
            ntStatus = STATUS_INVALID_NETWORK_RESPONSE;
            BAIL_ON_NT_STATUS(ntStatus);
        }

        ntStatus = SrvConnection2FindSession_SMB_V2(
                        pCtxSmb2,
                        pConnection,
                        pSmbRequest->pHeader->ullSessionId,
                        &pSession);
        BAIL_ON_NT_STATUS(ntStatus);

        ntStatus = SrvSetStatSession2Info(pExecContext, pSession);
        BAIL_ON_NT_STATUS(ntStatus);

        ntStatus = SMB2UnmarshalTreeConnect(
                        pSmbRequest,
                        &pTreeConnectHeader,
                        &wszPath);
        BAIL_ON_NT_STATUS(ntStatus);

        ntStatus = SrvBuildTreeConnectState_SMB_V2(
                        pConnection,
                        pSession,
                        pTreeConnectHeader,
                        &wszPath,
                        &pTConState);
        BAIL_ON_NT_STATUS(ntStatus);

        SRV_LOG_CALL_DEBUG(
                pExecContext->pLogContext,
                SMB_PROTOCOL_VERSION_2,
                pSmbRequest->pHeader->command,
                &SrvLogTreeConnect_SMB_V2,
                pTConState);

        pCtxSmb2->hState = pTConState;
        pCtxSmb2->pfnStateRelease = &SrvFreeTreeConnectStateHandle_SMB_V2;
    }

    switch (pTConState->stage)
    {
        case SRV_TREE_CONNECT_STAGE_SMB_V2_INITIAL:

            ntStatus = SrvGetShareName(pTConState->pwszPath, &pwszSharename);
            BAIL_ON_NT_STATUS(ntStatus);

            ntStatus = SrvShareFindByName(
                            pConnection->hShares,
                            pwszSharename,
                            pSession->pNetbiosDomain,
                            pSession->pSamAccountName,
                            &pShareInfo);
            if (ntStatus == STATUS_NOT_FOUND)
            {
                ntStatus = STATUS_BAD_NETWORK_NAME;
            }
            BAIL_ON_NT_STATUS(ntStatus);

            ntStatus = SrvSession2CreateTree(
                            pTConState->pSession,
                            pShareInfo,
                            &pTConState->pTree);
            BAIL_ON_NT_STATUS(ntStatus);

            // intentional fall through

        case SRV_TREE_CONNECT_STAGE_SMB_V2_CREATE_TREE_ROOT_HANDLE:

            pTConState->stage = SRV_TREE_CONNECT_STAGE_SMB_V2_BUILD_RESPONSE;

            if (pTConState->pTree->pShareInfo->Service ==
                                SHARE_STYPE_DISKTREE)
            {
                ntStatus = pExecContext->ioStatusBlock.Status;
                BAIL_ON_NT_STATUS(ntStatus);

                ntStatus = SrvCreateTreeRootHandle_SMB_V2(pExecContext);
                BAIL_ON_NT_STATUS(ntStatus);
            }

            // intentional fall through

        case SRV_TREE_CONNECT_STAGE_SMB_V2_BUILD_RESPONSE:

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

            ntStatus = SrvBuildTreeConnectResponse_SMB_V2(pExecContext);
            BAIL_ON_NT_STATUS(ntStatus);

	    ntStatus = SrvElementsRegisterResource(&pTConState->pTree->resource,
						   NULL);
	    BAIL_ON_NT_STATUS(ntStatus);

            // intentional fall through

        case SRV_TREE_CONNECT_STAGE_SMB_V2_DONE:

            // transfer tree so we do not run it down
            pCtxSmb2->pTree = pTConState->pTree;
            pTConState->pTree = NULL;

            break;
    }

cleanup:

    if (!NT_SUCCESS(ntStatus))
    {
        SrvLogTreeConnect2Failure(pExecContext, ntStatus);
    }

    if (pSession)
    {
        SrvSession2Release(pSession);
    }

    if (pShareInfo)
    {
        SrvShareReleaseInfo(pShareInfo);
    }

    SRV_SAFE_FREE_MEMORY(pwszSharename);

    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;

        case STATUS_OBJECT_NAME_NOT_FOUND:

            ntStatus = STATUS_BAD_NETWORK_PATH;

            // Intentional fall through

        default:

            SrvReleaseExecContextAsync(pExecContext);

            break;
    }

    goto cleanup;
}

static
NTSTATUS
SrvBuildTreeConnectState_SMB_V2(
    PLWIO_SRV_CONNECTION              pConnection,
    PLWIO_SRV_SESSION_2               pSession,
    PSMB2_TREE_CONNECT_REQUEST_HEADER pTreeConnectHeader,
    PUNICODE_STRING                   pPath,
    PSRV_TREE_CONNECT_STATE_SMB_V2*   ppTConState
    )
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    PSRV_TREE_CONNECT_STATE_SMB_V2 pTConState = NULL;

    ntStatus = SrvAllocateMemory(
                    sizeof(SRV_TREE_CONNECT_STATE_SMB_V2),
                    (PVOID*)&pTConState);
    BAIL_ON_NT_STATUS(ntStatus);

    pTConState->stage = SRV_TREE_CONNECT_STAGE_SMB_V2_INITIAL;

    pTConState->pRequestHeader = pTreeConnectHeader;

    ntStatus = SrvAllocateMemory(
                        pPath->Length + sizeof(wchar16_t),
                        (PVOID*)&pTConState->pwszPath);
    BAIL_ON_NT_STATUS(ntStatus);

    memcpy((PBYTE)pTConState->pwszPath, (PBYTE)pPath->Buffer, pPath->Length);

    pTConState->pSession = SrvSession2Acquire(pSession);

    *ppTConState = pTConState;

cleanup:

    return ntStatus;

error:

    *ppTConState = NULL;

    if (pTConState)
    {
        SrvFreeTreeConnectState_SMB_V2(pTConState);
    }

    goto cleanup;
}

static
NTSTATUS
SrvCreateTreeRootHandle_SMB_V2(
    PSRV_EXEC_CONTEXT pExecContext
    )
{
    NTSTATUS                       ntStatus    = STATUS_SUCCESS;
    PSRV_EXEC_CONTEXT_SMB_V2       pCtxSmb2    = pExecContext->pProtocolContext;
    PSRV_TREE_CONNECT_STATE_SMB_V2 pTConState  = NULL;
    BOOLEAN                        bShareInLock = FALSE;
    PWSTR pRawSharePath = NULL;
    PWSTR pShareName = NULL;

    pTConState = pCtxSmb2->hState;

    if (!RTL_STRING_NUM_CHARS(&pTConState->fileName.Name))
    {
        LWIO_LOCK_RWMUTEX_SHARED(
                    bShareInLock,
                    &pTConState->pTree->pShareInfo->Mutex);

        if (LwRtlCStringIsNullOrEmpty(pTConState->pTree->pShareInfo->pwszPath))
        {
            ntStatus = STATUS_INVALID_PARAMETER;
            BAIL_ON_NT_STATUS(ntStatus);
        }

        ntStatus = SrvShareGetPath(
                pTConState->pTree->pShareInfo,
                pTConState->pTree->pSession->pNetbiosDomain,
                pTConState->pTree->pSession->pSamAccountName,
                &pRawSharePath);
        BAIL_ON_NT_STATUS(ntStatus);

        ntStatus = SrvAllocateUnicodeStringW(
                        pRawSharePath,
                        &pTConState->fileName.Name);
        BAIL_ON_NT_STATUS(ntStatus);

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

        ntStatus = SrvShareGetName(
                pTConState->pTree->pShareInfo,
                pTConState->pTree->pSession->pNetbiosDomain,
                pTConState->pTree->pSession->pSamAccountName,
                &pShareName);
        BAIL_ON_NT_STATUS(ntStatus);

        // no need to lock share info here - the name is invariant
        ntStatus = SrvIoPrepareEcpList_SMB_V2(
                        pTConState->pSession,
                        pTConState->pTree,
                        pShareName,
                        &pTConState->pEcpList);
        BAIL_ON_NT_STATUS(ntStatus);
    }

    if (!pTConState->pTree->hFile)
    {
        SrvPrepareExecContextAsync(pExecContext);

        /* Explicitly pass NULL as exec context to avoid event */
        ntStatus = SrvIoCreateFile(
                        NULL,
                        pTConState->pTree->pShareInfo,
                        &pTConState->pTree->hFile,
                        pExecContext->pAcb,
                        &pExecContext->ioStatusBlock,
                        pTConState->pSession->pIoSecurityContext,
                        &pTConState->fileName,
                        pTConState->pSecurityDescriptor,
                        pTConState->pSecurityQOS,
                        FILE_READ_ATTRIBUTES,
                        0,
                        FILE_ATTRIBUTE_NORMAL,
                        FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
                        FILE_OPEN,
                        0,
                        NULL, /* EA Buffer */
                        0,    /* EA Length */
                        0,
                        &pTConState->pEcpList);
        BAIL_ON_NT_STATUS(ntStatus);

        SrvReleaseExecContextAsync(pExecContext); // completed sync
    }

cleanup:

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

    RTL_FREE(&pRawSharePath);
    RTL_FREE(&pShareName);

    return ntStatus;

error:

    goto cleanup;
}

static
NTSTATUS
SrvIoPrepareEcpList_SMB_V2(
    IN PLWIO_SRV_SESSION_2 pSession,
    IN PLWIO_SRV_TREE_2 pTree,
    IN PWSTR pwszShareName,
    IN OUT PIO_ECP_LIST* ppEcpList
    )
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    PUNICODE_STRING pShareName = NULL;

    if (!*ppEcpList)
    {
        ntStatus = IoRtlEcpListAllocate(ppEcpList);
        BAIL_ON_NT_STATUS(ntStatus);
    }

    ntStatus = IoRtlEcpListInsert(
                    *ppEcpList,
                    IO_ECP_TYPE_UNDERSTAND_OPEN_SNAPSHOT,
                    &pTree->UnderstandSnapshots,
                    sizeof(pTree->UnderstandSnapshots),
                    NULL);
    BAIL_ON_NT_STATUS(ntStatus);


    if (SrvElementsGetShareNameEcpEnabled())
    {
        ntStatus = RTL_ALLOCATE(&pShareName, UNICODE_STRING, sizeof(*pShareName));
        BAIL_ON_NT_STATUS(ntStatus);

        ntStatus = RtlUnicodeStringAllocateFromWC16String(
                        pShareName,
                        pwszShareName);
        BAIL_ON_NT_STATUS(ntStatus);

        ntStatus = IoRtlEcpListInsert(
                        *ppEcpList,
                        IO_ECP_TYPE_SMB_SHARE_NAME,
                        pShareName,
                        sizeof(*pShareName),
                        SrvIoFreeEcpString_SMB_V2);
        BAIL_ON_NT_STATUS(ntStatus);

        pShareName = NULL;
    }

    if (SrvElementsGetClientAddressEcpEnabled())
    {
        // Cast is necessary to discard const-ness.
        ntStatus = IoRtlEcpListInsert(
                        *ppEcpList,
                        IO_ECP_TYPE_CLIENT_ADDRESS,
                        (PVOID)pSession->pConnection->pClientAddress,
                        pSession->pConnection->clientAddrLen,
                        NULL);
        BAIL_ON_NT_STATUS(ntStatus);
    }

    if (SrvElementsGetOEMSessionEcpEnabled() && pSession->pOEMSession)
    {
        ntStatus = IoRtlEcpListInsert(
                        *ppEcpList,
                        IO_ECP_TYPE_SMB_OEM_SESSION,
                        &pSession->pOEMSession,
                        pSession->ulOEMSessionLength,
                        NULL);
        BAIL_ON_NT_STATUS(ntStatus);
    }

cleanup:

    RTL_FREE(&pShareName);

    return ntStatus;

error:

    goto cleanup;
}

static
VOID
SrvIoFreeEcpString_SMB_V2(
    IN PVOID pContext
    )
{
    PUNICODE_STRING pEcpString = (PUNICODE_STRING) pContext;

    if (pEcpString)
    {
        RtlUnicodeStringFree(pEcpString);
        RtlMemoryFree(pEcpString);
    }
}

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

    va_start(msgList, ulLine);
    pTConState = va_arg(msgList, PSRV_TREE_CONNECT_STATE_SMB_V2);

    if (pTConState && pTConState->pwszPath)
    {
        ntStatus = SrvWc16sToMbs(pTConState->pwszPath, &pszPath);
        BAIL_ON_NT_STATUS(ntStatus);
    }

    LW_RTL_LOG_RAW(
        logLevel,
        "srv",
        pszFunction,
        pszFile,
        ulLine,
        "TreeConnect Parameters: Path(%s)",
        LWIO_SAFE_LOG_STRING(pszPath));

error:

    va_end(msgList);

    SRV_SAFE_FREE_MEMORY(pszPath);

    return;
}

static
NTSTATUS
SrvBuildTreeConnectResponse_SMB_V2(
    PSRV_EXEC_CONTEXT pExecContext
    )
{
    NTSTATUS                   ntStatus     = STATUS_SUCCESS;
    PLWIO_SRV_CONNECTION       pConnection  = pExecContext->pConnection;
    PSRV_EXEC_CONTEXT_SMB_V2   pCtxSmb2     = pExecContext->pProtocolContext;
    ULONG                      iMsg         = pCtxSmb2->iMsg;
    PSRV_MESSAGE_SMB_V2        pSmbRequest  = &pCtxSmb2->pRequests[iMsg];
    PSRV_MESSAGE_SMB_V2        pSmbResponse = &pCtxSmb2->pResponses[iMsg];
    PSMB2_TREE_CONNECT_RESPONSE_HEADER pTreeConnectResponseHeader = NULL; // Do not free
    PSRV_TREE_CONNECT_STATE_SMB_V2     pTConState  = NULL;
    PBYTE   pOutBuffer       = pSmbResponse->pBuffer;
    ULONG   ulBytesAvailable = pSmbResponse->ulBytesAvailable;
    ULONG   ulOffset         = 0;
    ULONG   ulBytesUsed      = 0;
    ULONG   ulTotalBytesUsed = 0;

    pTConState = pCtxSmb2->hState;

    ntStatus = SMB2MarshalHeader(
                    pOutBuffer,
                    ulOffset,
                    ulBytesAvailable,
                    COM2_TREE_CONNECT,
                    pSmbRequest->CreditCharge,
                    pExecContext->usCreditsGranted,
                    pSmbRequest->pHeader->ulPid,
                    pSmbRequest->pHeader->ullCommandSequence,
                    pTConState->pTree->ulTid,
                    pTConState->pSession->ullUid,
                    0LL, /* Async Id */
                    STATUS_SUCCESS,
                    TRUE,
                    LwIsSetFlag(
                        pSmbRequest->pHeader->ulFlags,
                        SMB2_FLAGS_RELATED_OPERATION),
                    &pSmbResponse->pHeader,
                    &pSmbResponse->ulHeaderSize);
    BAIL_ON_NT_STATUS(ntStatus);

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

    ntStatus = SMB2MarshalTreeConnectResponse(
                    pOutBuffer,
                    ulOffset,
                    ulBytesAvailable,
                    pConnection,
                    pTConState->pTree,
                    &pTreeConnectResponseHeader,
                    &ulBytesUsed);
    BAIL_ON_NT_STATUS(ntStatus);

    // pTreeConnectResponseHeader->ulShareFlags |=
    //                         SMB2_SHARE_FLAGS_DFS;
    // pTreeConnectResponseHeader->ulShareFlags |=
    //                         SMB2_SHARE_FLAGS_DFS_ROOT;
    // pTreeConnectResponseHeader->ulShareFlags |=
    //                         SMB2_SHARE_FLAGS_RESTRICT_EXCL_OPENS;
    // pTreeConnectResponseHeader->ulShareFlags |=
    //                         SMB2_SHARE_FLAGS_FORCE_SHARED_DELETE;
    // pTreeConnectResponseHeader->ulShareFlags |=
    //                         SMB2_SHARE_FLAGS_ALLOW_NS_CACHING;
    // pTreeConnectResponseHeader->ulShareFlags |=
    //                         SMB2_SHARE_FLAGS_ACCESS_BASED_DIR_ENUM;

    switch (pTreeConnectResponseHeader->usShareType)
    {
        case SMB2_SHARE_TYPE_DISK:

            // pTreeConnectResponseHeader->ulShareFlags |=
            //                         SMB2_SHARE_FLAGS_FORCE_LEVELII_OPLOCK;

            break;

        case SMB2_SHARE_TYPE_NAMED_PIPE:

            pTreeConnectResponseHeader->ulShareFlags |=
                                    SMB2_SHARE_FLAGS_CSC_NONE;

            break;

        default:

            break;

    }

    // pTreeConnectResponseHeader->ulShareFlags |=
    //                         SMB2_SHARE_FLAGS_ENABLE_HASH;

    // pTreeConnectRequestHeader->ulShareCapabilities |=
    //                             SMB2_SHARE_CAPABILITIES_DFS_AVAILABLE;

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

    pSmbResponse->ulMessageSize = ulTotalBytesUsed;

cleanup:

    return ntStatus;

error:

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

    pSmbResponse->ulMessageSize = 0;

    goto cleanup;
}

static
VOID
SrvFreeTreeConnectStateHandle_SMB_V2(
    HANDLE hTConState
    )
{
    SrvFreeTreeConnectState_SMB_V2(hTConState);
}

static
VOID
SrvFreeTreeConnectState_SMB_V2(
    PSRV_TREE_CONNECT_STATE_SMB_V2 pTConState
    )
{
    if (pTConState->pEcpList)
    {
        IoRtlEcpListFree(&pTConState->pEcpList);
    }

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

    SRV_FREE_UNICODE_STRING(&pTConState->fileName.Name);

    if (pTConState->pwszPath)
    {
        SrvFreeMemory(pTConState->pwszPath);
    }

    if (pTConState->pSession)
    {
        SrvSession2Release(pTConState->pSession);
    }

    // if non-NULL, it is left over and must be run down
    if (pTConState->pTree)
    {
        SrvTree2Rundown(pTConState->pTree);
        SrvTree2Release(pTConState->pTree);
    }

    SrvFreeMemory(pTConState);
}

