/* -*- 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
SrvBuildTreeConnectState(
    PLWIO_SRV_CONNECTION            pConnection,
    PTREE_CONNECT_REQUEST_HEADER    pRequestHeader,
    PBYTE                           pszPassword,
    PBYTE                           pszService,
    PWSTR                           pwszPath,
    PSRV_TREE_CONNECT_STATE_SMB_V1* ppTConState
    );

static
NTSTATUS
SrvQueryTreeConnectInfo(
    PSRV_EXEC_CONTEXT pExecContext
    );

static
NTSTATUS
SrvCreateTreeRootHandle(
    PSRV_EXEC_CONTEXT pExecContext
    );

static
NTSTATUS
SrvIoPrepareEcpList(
    IN PLWIO_SRV_SESSION pSession,
    IN PLWIO_SRV_TREE pTree,
    IN PWSTR pwszShareName,
    IN OUT PIO_ECP_LIST* ppEcpList
    );

static
VOID
SrvIoFreeEcpString(
    IN PVOID pContext
    );

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

static
NTSTATUS
SrvBuildTreeConnectResponse(
    PSRV_EXEC_CONTEXT pExecContext
    );

static
NTSTATUS
SrvGetServiceName(
    PSRV_SHARE_INFO pShareInfo,
    PSTR*           ppszService
    );

static
NTSTATUS
SrvGetNativeFilesystem(
    PSRV_EXEC_CONTEXT pExecContext
    );

static
VOID
SrvFreeTreeConnectStateHandle(
    HANDLE hTConState
    );

static
VOID
SrvFreeTreeConnectState(
    PSRV_TREE_CONNECT_STATE_SMB_V1 pTConState
    );

static
VOID
SrvLogTreeConnectFailure(
    PSRV_EXEC_CONTEXT pExecContext,
    NTSTATUS Status
    )
{
    PSRV_EXEC_CONTEXT_SMB_V1       pCtxSmb1   = pExecContext->pProtocolContext;
    PSRV_TREE_CONNECT_STATE_SMB_V1 pTConState = pCtxSmb1->hState;

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

NTSTATUS
SrvProcessTreeConnectAndX(
    PSRV_EXEC_CONTEXT pExecContext
    )
{
    NTSTATUS                   ntStatus     = 0;
    PLWIO_SRV_CONNECTION       pConnection  = pExecContext->pConnection;
    PSRV_EXEC_CONTEXT_SMB_V1   pCtxSmb1     = pExecContext->pProtocolContext;
    PSRV_TREE_CONNECT_STATE_SMB_V1 pTConState    = pCtxSmb1->hState;
    PLWIO_SRV_SESSION              pSession      = NULL;
    PWSTR                          pwszSharename = NULL;
    PWSTR                          pwszPath      = NULL;
    PSRV_SHARE_INFO pShareInfo = NULL;

    if (pTConState == NULL)
    {
        ULONG               iMsg         = pCtxSmb1->iMsg;
        PSRV_MESSAGE_SMB_V1 pSmbRequest  = &pCtxSmb1->pRequests[iMsg];
        PBYTE pBuffer  = pSmbRequest->pBuffer + pSmbRequest->usHeaderSize;
        ULONG ulBytesAvailable = pSmbRequest->ulMessageSize - pSmbRequest->usHeaderSize;
        PTREE_CONNECT_REQUEST_HEADER  pRequestHeader = NULL; // Do not free
        PBYTE                         pszPassword    = NULL; // Do not free
        PBYTE                         pszService     = NULL; // Do not free

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

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

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

        ntStatus = UnmarshallTreeConnectRequest(
                        pSmbRequest->pHeader,
                        pBuffer,
                        ulBytesAvailable,
                        &pRequestHeader,
                        &pszPassword,
                        &pwszPath,
                        &pszService);
        BAIL_ON_NT_STATUS(ntStatus);

        SRV_LOG_CALL_DEBUG(
                pExecContext->pLogContext,
                SMB_PROTOCOL_VERSION_1,
                pSmbRequest->pHeader->command,
                &SrvLogTreeConnect_SMB_V1,
                pRequestHeader,
                pszPassword,
                pwszPath,
                pszService);

        if (pRequestHeader->flags & SMB_TREE_CONNECT_DISCONNECT_TID)
        {
            NTSTATUS ntStatus2 = 0;
            PLWIO_SRV_TREE pTree = NULL;

            ntStatus2 = SrvSessionFindTree(
                            pSession,
                            pSmbRequest->pHeader->tid,
                            &pTree);
            BAIL_ON_NT_STATUS(ntStatus);

            if (ntStatus2)
            {
                LWIO_LOG_ERROR("Failed to remove tid [%u] from session [uid=%u]. [code:%d]",
                                pSmbRequest->pHeader->tid,
                                pSession->uid,
                                ntStatus2);
            }

            if (pTree)
            {
                SrvTreeRundown(pTree);
                SrvTreeRelease(pTree);
            }
        }

        ntStatus = SrvBuildTreeConnectState(
                        pConnection,
                        pRequestHeader,
                        pszPassword,
                        pszService,
                        pwszPath,
                        &pTConState);
        BAIL_ON_NT_STATUS(ntStatus);

        pwszPath = NULL;
        pCtxSmb1->hState = pTConState;
        pCtxSmb1->pfnStateRelease = &SrvFreeTreeConnectStateHandle;

        pTConState->pSession = SrvSessionAcquire(pSession);
    }

    switch (pTConState->stage)
    {
        case SRV_TREE_CONNECT_STAGE_SMB_V1_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 = SrvSessionCreateTree(
                            pSession,
                            pShareInfo,
                            &pTConState->pTree);
            BAIL_ON_NT_STATUS(ntStatus);

            pTConState->stage =
                    SRV_TREE_CONNECT_STAGE_SMB_V1_CREATE_TREE_ROOT_HANDLE;

            // Intentional fall through

        case SRV_TREE_CONNECT_STAGE_SMB_V1_CREATE_TREE_ROOT_HANDLE:

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

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

            pTConState->stage =
                    SRV_TREE_CONNECT_STAGE_SMB_V1_ATTEMPT_QUERY_INFO;

            // Intentional fall through

        case SRV_TREE_CONNECT_STAGE_SMB_V1_ATTEMPT_QUERY_INFO:

            // Catch failed CreateFile calls when they come back around

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

            ntStatus = SrvQueryTreeConnectInfo(pExecContext);
            BAIL_ON_NT_STATUS(ntStatus);

            pTConState->stage =
                    SRV_TREE_CONNECT_STAGE_SMB_V1_QUERY_INFO_COMPLETED;

            // Intentional fall through

        case SRV_TREE_CONNECT_STAGE_SMB_V1_QUERY_INFO_COMPLETED:

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

            ntStatus = SrvBuildTreeConnectResponse(pExecContext);
            BAIL_ON_NT_STATUS(ntStatus);

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

            pTConState->stage = SRV_TREE_CONNECT_STAGE_SMB_V1_DONE;

            // Intentional fall through

        case SRV_TREE_CONNECT_STAGE_SMB_V1_DONE:

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

            break;
    }

cleanup:

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

    if (pSession)
    {
        SrvSessionRelease(pSession);
    }

    if (pwszSharename)
    {
        SrvFreeMemory(pwszSharename);
    }

    if (pShareInfo)
    {
        SrvShareReleaseInfo(pShareInfo);
    }

    RtlWC16StringFree(&pwszPath);

    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(
    PLWIO_SRV_CONNECTION            pConnection,
    PTREE_CONNECT_REQUEST_HEADER    pRequestHeader,
    PBYTE                           pszPassword,
    PBYTE                           pszService,
    PWSTR                           pwszPath,
    PSRV_TREE_CONNECT_STATE_SMB_V1* ppTConState
    )
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    PSRV_TREE_CONNECT_STATE_SMB_V1 pTConState = NULL;

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

    pTConState->stage = SRV_TREE_CONNECT_STAGE_SMB_V1_INITIAL;

    pTConState->pRequestHeader = pRequestHeader;
    pTConState->pszPassword    = pszPassword;
    pTConState->pszService     = pszService;
    pTConState->pwszPath       = pwszPath;

    *ppTConState = pTConState;

cleanup:

    return ntStatus;

error:

    *ppTConState = NULL;

    if (pTConState)
    {
        SrvFreeTreeConnectState(pTConState);
    }

    goto cleanup;
}

static
NTSTATUS
SrvQueryTreeConnectInfo(
    PSRV_EXEC_CONTEXT pExecContext
    )
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    PSRV_EXEC_CONTEXT_SMB_V1       pCtxSmb1   = pExecContext->pProtocolContext;
    PSRV_TREE_CONNECT_STATE_SMB_V1 pTConState = pCtxSmb1->hState;

    if (!pTConState->pszService2)
    {
        ntStatus = SrvGetServiceName(
                        pTConState->pTree->pShareInfo,
                        &pTConState->pszService2);
        BAIL_ON_NT_STATUS(ntStatus);
    }

    if ((pTConState->pTree->pShareInfo->Service == SHARE_STYPE_DISKTREE) &&
        (!pTConState->pwszNativeFileSystem))
    {
        ntStatus = SrvGetNativeFilesystem(pExecContext);
        BAIL_ON_NT_STATUS(ntStatus);
    }

    ntStatus = SrvGetMaximalShareAccessMask(
                    pTConState->pTree->pShareInfo,
                    &pTConState->ulMaximalShareAccessMask);
    BAIL_ON_NT_STATUS(ntStatus);

    ntStatus = SrvGetGuestShareAccessMask(
                    pTConState->pTree->pShareInfo,
                    &pTConState->ulGuestMaximalShareAccessMask);
    BAIL_ON_NT_STATUS(ntStatus);

    ntStatus = SrvGetCscFlags(
                    pTConState->pTree->pShareInfo,
                    &pTConState->usCSCFlags);
    BAIL_ON_NT_STATUS(ntStatus);

cleanup:

    return ntStatus;

error:

    goto cleanup;
}

static
NTSTATUS
SrvCreateTreeRootHandle(
    PSRV_EXEC_CONTEXT pExecContext
    )
{
    NTSTATUS                       ntStatus     = STATUS_SUCCESS;
    PSRV_EXEC_CONTEXT_SMB_V1       pCtxSmb1     = pExecContext->pProtocolContext;
    PSRV_TREE_CONNECT_STATE_SMB_V1 pTConState   = pCtxSmb1->hState;
    BOOLEAN                        bShareInLock = FALSE;
    PWSTR pRawSharePath = NULL;
    PWSTR pShareName = NULL;

    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(
                        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 synchronously
    }

cleanup:

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

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

    return ntStatus;

error:

    goto cleanup;
}

static
NTSTATUS
SrvIoPrepareEcpList(
    IN PLWIO_SRV_SESSION pSession,
    IN PLWIO_SRV_TREE 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);
        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(
    IN PVOID pContext
    )
{
    PUNICODE_STRING pEcpString = (PUNICODE_STRING) pContext;

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

static
VOID
SrvLogTreeConnect_SMB_V1(
    PSRV_LOG_CONTEXT pLogContext,
    LWIO_LOG_LEVEL   logLevel,
    PCSTR            pszFunction,
    PCSTR            pszFile,
    ULONG            ulLine,
    ...
    )
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    PTREE_CONNECT_REQUEST_HEADER pRequestHeader = NULL;
    PWSTR   pwszPath    = NULL;
    PSTR    pszService  = NULL;
    PSTR    pszPath     = NULL;
    va_list msgList;

    va_start(msgList, ulLine);
    pRequestHeader = (PTREE_CONNECT_REQUEST_HEADER)va_arg(msgList, PTREE_CONNECT_REQUEST_HEADER);
    // Discard password
    (void) va_arg(msgList, PSTR);
    pwszPath    = (PWSTR)va_arg(msgList, PWSTR);
    pszService  = (PSTR)va_arg(msgList, PSTR);

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

    LW_RTL_LOG_RAW(
        logLevel,
        "srv",
        pszFunction,
        pszFile,
        ulLine,
        "TreeConnect Parameters: Flags(0x%x),Service(%s),Path(%s)",
        pRequestHeader->flags,
        LWIO_SAFE_LOG_STRING(pszService),
        LWIO_SAFE_LOG_STRING(pszPath));

error:

    va_end(msgList);

    SRV_SAFE_FREE_MEMORY(pszPath);

    return;
}

static
NTSTATUS
SrvBuildTreeConnectResponse(
    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_TREE_CONNECT_STATE_SMB_V1 pTConState   = pCtxSmb1->hState;
    PSRV_MESSAGE_SMB_V1            pSmbResponse = &pCtxSmb1->pResponses[iMsg];
    PTREE_CONNECT_RESPONSE_HEADER  pResponseHeader = NULL; // Do not free
    PTREE_CONNECT_EXT_RESPONSE_HEADER  pExtResponseHeader = NULL; // Do not free
    PBYTE  pOutBuffer       = pSmbResponse->pBuffer;
    ULONG  ulBytesAvailable = pSmbResponse->ulBytesAvailable;
    ULONG  ulOffset         = 0;
    USHORT usBytesUsed      = 0;
    ULONG  ulTotalBytesUsed = 0;
    ULONG  ulHeaderOffset = 0;
    BOOLEAN bTConExt = FALSE;

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

        // Must set the new TID in the SMB Header if this is a chained
        // request.  E.g. a chained session setup & tcon from jCIFS.
        pSmbResponse->pHeader->tid = pTConState->pTree->tid;
    }

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

    if (pTConState->pRequestHeader->flags & SMB_TREE_CONNECT_EXT_RESPONSE)
    {
        *pSmbResponse->pWordCount = 7;
        pExtResponseHeader = (PTREE_CONNECT_EXT_RESPONSE_HEADER)pOutBuffer;
        ulHeaderOffset = sizeof(TREE_CONNECT_EXT_RESPONSE_HEADER);
        bTConExt = TRUE;
    }
    else
    {
        *pSmbResponse->pWordCount = 3;
        pResponseHeader = (PTREE_CONNECT_RESPONSE_HEADER)pOutBuffer;
        ulHeaderOffset = sizeof(TREE_CONNECT_RESPONSE_HEADER);
    }

    if (ulBytesAvailable < ulHeaderOffset)
    {
        ntStatus = STATUS_INVALID_BUFFER_SIZE;
        BAIL_ON_NT_STATUS(ntStatus);
    }

    pOutBuffer       += ulHeaderOffset;
    ulOffset         += ulHeaderOffset;
    ulBytesAvailable -= ulHeaderOffset;
    ulTotalBytesUsed += ulHeaderOffset;

    if (bTConExt)
    {
        pExtResponseHeader->optionalSupport = pTConState->usCSCFlags;
        pExtResponseHeader->maximalShareAccessMask =
                                pTConState->ulMaximalShareAccessMask;

        pExtResponseHeader->guestMaximalShareAccessMask =
                                pTConState->ulGuestMaximalShareAccessMask;
    }
    else
    {
        pResponseHeader->optionalSupport = pTConState->usCSCFlags;
    }

    ntStatus = MarshallTreeConnectResponseData(
                    pOutBuffer,
                    ulBytesAvailable,
                    ulOffset,
                    &usBytesUsed,
                    (const PBYTE)pTConState->pszService2,
                    pTConState->pwszNativeFileSystem);
    BAIL_ON_NT_STATUS(ntStatus);

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

    if (bTConExt)
    {
        pExtResponseHeader->byteCount = usBytesUsed;
    }
    else
    {
        pResponseHeader->byteCount = usBytesUsed;
    }

    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
SrvGetServiceName(
    PSRV_SHARE_INFO pShareInfo,
    PSTR* ppszService
    )
{
    NTSTATUS ntStatus = 0;
    BOOLEAN bInLock = FALSE;
    PSTR pszService = NULL;

    LWIO_LOCK_RWMUTEX_SHARED(bInLock, &pShareInfo->Mutex);

    ntStatus = SrvShareMapIdToServiceStringA(
                    pShareInfo->Service,
                    &pszService);
    BAIL_ON_NT_STATUS(ntStatus);

    *ppszService = pszService;

cleanup:

    LWIO_UNLOCK_RWMUTEX(bInLock, &pShareInfo->Mutex);

    return ntStatus;

error:

    *ppszService = NULL;

    goto cleanup;
}

static
NTSTATUS
SrvGetNativeFilesystem(
    PSRV_EXEC_CONTEXT pExecContext
    )
{
    NTSTATUS                       ntStatus    = STATUS_SUCCESS;
    PSRV_EXEC_CONTEXT_SMB_V1       pCtxSmb1    = pExecContext->pProtocolContext;
    PSRV_TREE_CONNECT_STATE_SMB_V1 pTConState  = pCtxSmb1->hState;
    PFILE_FS_ATTRIBUTE_INFORMATION pFsAttrInfo = NULL;
    BOOLEAN  bContinue = TRUE;

    if (!pTConState->pVolumeInfo)
    {
        pExecContext->ioStatusBlock.Status = STATUS_BUFFER_TOO_SMALL;
    }

    do
    {
        ntStatus = pExecContext->ioStatusBlock.Status;

        switch (ntStatus)
        {
            case STATUS_SUCCESS:

                bContinue = FALSE;

                break;

            case STATUS_BUFFER_TOO_SMALL:
                {
                    USHORT usNewSize =  pTConState->usBytesAllocated +
                                        256 * sizeof(wchar16_t);

                    ntStatus = SrvReallocMemory(
                                    pTConState->pVolumeInfo,
                                    usNewSize,
                                    (PVOID*)&pTConState->pVolumeInfo);
                    BAIL_ON_NT_STATUS(ntStatus);

                    pTConState->usBytesAllocated = usNewSize;
                }

                break;
        }
        BAIL_ON_NT_STATUS(ntStatus);

        if (!bContinue)
        {
            break;
        }

        SrvPrepareExecContextAsync(pExecContext);

        ntStatus = IoQueryVolumeInformationFile(
                        pTConState->pTree->hFile,
                        pExecContext->pAcb,
                        &pExecContext->ioStatusBlock,
                        pTConState->pVolumeInfo,
                        pTConState->usBytesAllocated,
                        FileFsAttributeInformation);
        if (ntStatus == STATUS_BUFFER_TOO_SMALL)
        {
            SrvReleaseExecContextAsync(pExecContext);

            continue;
        }
        else if (ntStatus == STATUS_SUCCESS)
        {
            SrvReleaseExecContextAsync(pExecContext);

            bContinue = FALSE;

            break;
        }
        BAIL_ON_NT_STATUS(ntStatus);

    } while (bContinue);

    pFsAttrInfo = (PFILE_FS_ATTRIBUTE_INFORMATION)pTConState->pVolumeInfo;

    if (!pFsAttrInfo->FileSystemNameLength)
    {
        ntStatus = STATUS_INVALID_PARAMETER;
        BAIL_ON_NT_STATUS(ntStatus);
    }

    ntStatus = SrvAllocateMemory(
                    pFsAttrInfo->FileSystemNameLength + sizeof(wchar16_t),
                    (PVOID*)&pTConState->pwszNativeFileSystem);
    BAIL_ON_NT_STATUS(ntStatus);

    memcpy((PBYTE)pTConState->pwszNativeFileSystem,
           (PBYTE)pFsAttrInfo->FileSystemName,
           pFsAttrInfo->FileSystemNameLength);

cleanup:

    return ntStatus;

error:

    goto cleanup;
}

static
VOID
SrvFreeTreeConnectStateHandle(
    HANDLE hTConState
    )
{
    SrvFreeTreeConnectState((PSRV_TREE_CONNECT_STATE_SMB_V1)hTConState);
}

static
VOID
SrvFreeTreeConnectState(
    PSRV_TREE_CONNECT_STATE_SMB_V1 pTConState
    )
{
    if (pTConState->pEcpList)
    {
        IoRtlEcpListFree(&pTConState->pEcpList);
    }

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

    SRV_FREE_UNICODE_STRING(&pTConState->fileName.Name);

    RtlWC16StringFree(&pTConState->pwszPath);

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

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

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

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

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

    SrvFreeMemory(pTConState);
}
