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

typedef struct _SMB_FILE_STREAM_INFO_HEADER
{
    ULONG  ulNextEntryOffset;
    ULONG  ulStreamNameLength;
    LONG64 llStreamSize;
    LONG64 llStreamAllocationSize;
    WCHAR  StreamName[];
} __attribute__((__packed__)) SMB_FILE_STREAM_INFO_HEADER,
                             *PSMB_FILE_STREAM_INFO_HEADER;

static GET_INFO_CLASS_GET_FUNC SrvGetFileBasicInfo;
static GET_INFO_CLASS_GET_FUNC SrvGetFileNetworkOpenInfo;
static GET_INFO_CLASS_GET_FUNC SrvGetFullEas;

static GET_INFO_CLASS_MARSHAL_FUNC SrvMarshalFileBasicInfo_SMB_V1;
static GET_INFO_CLASS_MARSHAL_FUNC SrvMarshalFileStandardInfo_SMB_V1;
static GET_INFO_CLASS_MARSHAL_FUNC SrvMarshalFileEaInfo_SMB_V1;
static GET_INFO_CLASS_MARSHAL_FUNC SrvMarshalFileStreamInfo_SMB_V1;
static GET_INFO_CLASS_MARSHAL_FUNC SrvMarshalFileAllInfo_SMB_V1;
static GET_INFO_CLASS_MARSHAL_FUNC SrvMarshalFileNameInfo_SMB_V1;
static GET_INFO_CLASS_MARSHAL_FUNC SrvMarshalFileInternalInfo_SMB_V1;
static GET_INFO_CLASS_MARSHAL_FUNC SrvMarshalFileNetworkOpenInfo_SMB_V1;
static GET_INFO_CLASS_MARSHAL_FUNC SrvMarshalFileSmbStandardlInfo_SMB_V1;
static GET_INFO_CLASS_MARSHAL_FUNC SrvMarshalFileAccessInfo_SMB_V1;
static GET_INFO_CLASS_MARSHAL_FUNC SrvMarshalFileAttributeTagInfo_SMB_V1;
static GET_INFO_CLASS_MARSHAL_FUNC SrvMarshalFileEaSize_SMB_V1;
static GET_INFO_CLASS_MARSHAL_FUNC SrvMarshalEas_SMB_V1;

static
NTSTATUS
SrvMarshalFileName_SMB_V1(
    PLWIO_SRV_TREE pTree,
    PWSTR pwszFileName,
    PULONG pulFileNameLength,
    PBYTE pOutBuffer,
    ULONG ulBytesAvailable,
    PULONG pulBytesUsed
    );

static GET_INFO_CLASS_DISPATCH_ENTRY Trans2InfoClassDispatchTable[] =
{
    [SMB_QUERY_FILE_BASIC_INFO]             =
    {
        SrvGetFileBasicInfo,
        SrvMarshalFileBasicInfo_SMB_V1,
        FileBasicInformation,
        sizeof(TRANS2_FILE_BASIC_INFORMATION),
        sizeof(FILE_BASIC_INFORMATION)
    },
    [SMB_QUERY_FILE_BASIC_INFO_ALIAS]       =
    {
        SrvGetFileBasicInfo,
        SrvMarshalFileBasicInfo_SMB_V1,
        FileBasicInformation,
        sizeof(TRANS2_FILE_BASIC_INFORMATION),
        sizeof(FILE_BASIC_INFORMATION)
    },

    [SMB_QUERY_FILE_STANDARD_INFO]          =
    {
        SrvGetFileInfoGeneric,
        SrvMarshalFileStandardInfo_SMB_V1,
        FileStandardInformation,
        sizeof(TRANS2_FILE_STANDARD_INFORMATION),
        sizeof(FILE_STANDARD_INFORMATION)
    },
    [SMB_QUERY_FILE_STANDARD_INFO_ALIAS]    =
    {
        SrvGetFileInfoGeneric,
        SrvMarshalFileStandardInfo_SMB_V1,
        FileStandardInformation,
        sizeof(TRANS2_FILE_STANDARD_INFORMATION),
        sizeof(FILE_STANDARD_INFORMATION)
    },

    [SMB_QUERY_FILE_EA_INFO]                =
    {
        SrvGetFileInfoGeneric,
        SrvMarshalFileEaInfo_SMB_V1,
        FileEaInformation,
        sizeof(TRANS2_FILE_EA_INFORMATION),
        sizeof(FILE_EA_INFORMATION)
    },
    [SMB_QUERY_FILE_EA_INFO_ALIAS]          =
    {
        SrvGetFileInfoGeneric,
        SrvMarshalFileEaInfo_SMB_V1,
        FileEaInformation,
        sizeof(TRANS2_FILE_EA_INFORMATION),
        sizeof(FILE_EA_INFORMATION)
    },

    [SMB_QUERY_FILE_STREAM_INFO]            =
    {
        SrvGetFileInfoGeneric_WithString,
        SrvMarshalFileStreamInfo_SMB_V1,
        FileStreamInformation,
        sizeof(SMB_FILE_STREAM_INFO_HEADER),
        sizeof(FILE_STREAM_INFORMATION)
    },
    [SMB_QUERY_FILE_STREAM_INFO_ALIAS]      =
    {
        SrvGetFileInfoGeneric_WithString,
        SrvMarshalFileStreamInfo_SMB_V1,
        FileStreamInformation,
        sizeof(SMB_FILE_STREAM_INFO_HEADER),
        sizeof(FILE_STREAM_INFORMATION)
    },

    [SMB_QUERY_FILE_ALL_INFO]               =
    {
        SrvGetFileInfoGeneric_WithString,
        SrvMarshalFileAllInfo_SMB_V1,
        FileAllInformation,
        sizeof(TRANS2_FILE_ALL_INFORMATION),
        LW_FIELD_OFFSET(FILE_ALL_INFORMATION, NameInformation.FileName)
    },
    [SMB_QUERY_FILE_ALL_INFO_ALIAS]         =
    {
        SrvGetFileInfoGeneric_WithString,
        SrvMarshalFileAllInfo_SMB_V1,
        FileAllInformation,
        sizeof(TRANS2_FILE_ALL_INFORMATION),
        LW_FIELD_OFFSET(FILE_ALL_INFORMATION, NameInformation.FileName)
    },

    [SMB_QUERY_FILE_NAME_INFO]              =
    {
        SrvGetFileInfoGeneric_WithFileName,
        SrvMarshalFileNameInfo_SMB_V1,
        FileNameInformation,
        sizeof(TRANS2_FILE_NAME_INFORMATION),
        sizeof(FILE_NAME_INFORMATION)
    },
    [SMB_QUERY_FILE_NAME_INFO_ALIAS]        =
    {
        SrvGetFileInfoGeneric_WithFileName,
        SrvMarshalFileNameInfo_SMB_V1,
        FileNameInformation,
        sizeof(TRANS2_FILE_NAME_INFORMATION),
        sizeof(FILE_NAME_INFORMATION)
    },

    [SMB_QUERY_FILE_ALT_NAME_INFO]          =
    {
        SrvGetFileInfoGeneric_WithFileName,
        SrvMarshalFileNameInfo_SMB_V1,
        FileAlternateNameInformation,
        sizeof(TRANS2_FILE_NAME_INFORMATION),
        sizeof(FILE_NAME_INFORMATION)
    },
    [SMB_QUERY_FILE_ALT_NAME_INFO_ALIAS]    =
    {
        SrvGetFileInfoGeneric_WithString,
        SrvMarshalFileNameInfo_SMB_V1,
        FileAlternateNameInformation,
        sizeof(TRANS2_FILE_NAME_INFORMATION),
        sizeof(FILE_NAME_INFORMATION)
    },

    [SMB_QUERY_FILE_INTERNAL_INFO]          =
    {
        SrvGetFileInfoGeneric,
        SrvMarshalFileInternalInfo_SMB_V1,
        FileInternalInformation,
        sizeof(TRANS2_FILE_INTERNAL_INFORMATION),
        sizeof(FILE_INTERNAL_INFORMATION)
    },

    [SMB_QUERY_FILE_NETWORK_OPEN_INFO]      =
    {
        SrvGetFileNetworkOpenInfo,
        SrvMarshalFileNetworkOpenInfo_SMB_V1,
        FileNetworkOpenInformation,
        sizeof(TRANS2_FILE_NETWORK_OPEN_INFORMATION),
        sizeof(FILE_NETWORK_OPEN_INFORMATION)
    },

    [SMB_INFO_STANDARD]                     =
    {
        SrvGetFileInfoGeneric_WithString,
        SrvMarshalFileSmbStandardlInfo_SMB_V1,
        FileAllInformation,
        sizeof(TRANS2_FILE_SMB_STANDARD_INFORMATION),
        sizeof(FILE_ALL_INFORMATION)
    },

    [SMB_QUERY_FILE_ACCESS_INFO]            =
    {
        SrvGetFileInfoGeneric,
        SrvMarshalFileAccessInfo_SMB_V1,
        FileAccessInformation,
        sizeof(TRANS2_FILE_ACCESS_INFORMATION),
        sizeof(FILE_ACCESS_INFORMATION)
    },

    [SMB_QUERY_FILE_ATTRIBUTE_TAG_INFO]     =
    {
        SrvGetFileInfoGeneric,
        SrvMarshalFileAttributeTagInfo_SMB_V1,
        FileAttributeTagInformation,
        sizeof(TRANS2_FILE_ATTRIBUTE_TAG_INFORMATION),
        sizeof(FILE_ATTRIBUTE_TAG_INFORMATION)
    },

    [SMB_INFO_QUERY_EA_SIZE]                =
    {
        SrvGetFileInfoGeneric_WithString,
        SrvMarshalFileEaSize_SMB_V1,
        FileAllInformation,
        sizeof(TRANS2_FILE_SMB_EA_SIZE),
        sizeof(FILE_ALL_INFORMATION)
    },

    [SMB_INFO_QUERY_EAS_FROM_LIST]          =
    {
        SrvGetFullEas,
        SrvMarshalEas_SMB_V1,
        /*unused*/0,
        /*unused*/0,
        /*unused*/0
    },

    [SMB_INFO_QUERY_ALL_EAS]                =
    {
        SrvGetFullEas,
        SrvMarshalEas_SMB_V1,
        /*unused*/0,
        /*unused*/0,
        /*unused*/0
    },

    /* Currently unsuported but not invalid */
    [SMB_INFO_IS_NAME_VALID]                =
    {
        SrvGetUnsupportedInfo,
        SrvMarshalUnsupportedInfo,
    },
    [SMB_QUERY_FILE_COMPRESSION_INFO]       =
    {
        SrvGetUnsupportedInfo,
        SrvMarshalUnsupportedInfo,
    },
    [SMB_QUERY_FILE_COMPRESSION_INFO_ALIAS] =
    {
        SrvGetUnsupportedInfo,
        SrvMarshalUnsupportedInfo,
    },
    [SMB_QUERY_FILE_UNIX_BASIC]             =
    {
        SrvGetUnsupportedInfo,
        SrvMarshalUnsupportedInfo,
    },
    [SMB_QUERY_FILE_UNIX_LINK]              =
    {
        SrvGetUnsupportedInfo,
        SrvMarshalUnsupportedInfo,
    },
    [SMB_QUERY_FILE_POSITION_INFO]          =
    {
        SrvGetUnsupportedInfo,
        SrvMarshalUnsupportedInfo,
    },
    [SMB_QUERY_FILE_MODE_INFO]              =
    {
        SrvGetUnsupportedInfo,
        SrvMarshalUnsupportedInfo,
    },
    [SMB_QUERY_FILE_ALIGNMENT_INFO]         =
    {
        SrvGetUnsupportedInfo,
        SrvMarshalUnsupportedInfo,
    }
};

static
NTSTATUS
SrvGetFileBasicInfo(
    PSRV_EXEC_CONTEXT pExecContext,
    PSRV_PROCESS_INFO_STATE pInfoState,
    PGET_INFO_CLASS_DISPATCH_ENTRY pInfoEntry
    )
{
    NTSTATUS                   ntStatus     = 0;
    PSRV_EXEC_CONTEXT_SMB_V1   pCtxSmb1     = pExecContext->pProtocolContext;
    PSRV_TRANS2_STATE_SMB_V1   pTrans2State = pCtxSmb1->hState;
    PFILE_BASIC_INFORMATION    pInfo        = NULL;

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

    pInfo = (PFILE_BASIC_INFORMATION)pInfoState->pData;
    if (!pInfo)
    {
        ntStatus = SrvAllocateMemory(sizeof(*pInfo), OUT_PPVOID(&pInfo));
        BAIL_ON_NT_STATUS(ntStatus);

        pInfoState->pData = (PBYTE)pInfo;
        pInfoState->ulDataLen = sizeof(*pInfo);

        if (IoRtlEcpListIsAcknowledged(
                pTrans2State->pEcpList,
                IO_ECP_TYPE_NET_OPEN_INFO))
        {
            PFILE_NETWORK_OPEN_INFORMATION pNetworkOpenInfo =
                pTrans2State->pNetworkOpenInfo;

            pInfo->CreationTime   = pNetworkOpenInfo->CreationTime;
            pInfo->LastAccessTime = pNetworkOpenInfo->LastAccessTime;
            pInfo->LastWriteTime  = pNetworkOpenInfo->LastWriteTime;
            pInfo->ChangeTime     = pNetworkOpenInfo->ChangeTime;
            pInfo->FileAttributes = pNetworkOpenInfo->FileAttributes;

            pExecContext->ioStatusBlock.Status = STATUS_SUCCESS;
            pExecContext->ioStatusBlock.BytesTransferred = sizeof(*pInfo);
        }
        else
        {
            SrvPrepareExecContextAsync(pExecContext);

            ntStatus = IoQueryInformationFile(
                            pInfoState->hFile,
                            pExecContext->pAcb,
                            &pExecContext->ioStatusBlock,
                            pInfoState->pData,
                            pInfoState->ulDataLen,
                            FileBasicInformation);
            BAIL_ON_NT_STATUS(ntStatus);

            SrvReleaseExecContextAsync(pExecContext); // completed synchronously
        }
    }

error:

    return ntStatus;
}

static
NTSTATUS
SrvGetFileNetworkOpenInfo(
    PSRV_EXEC_CONTEXT               pExecContext,
    PSRV_PROCESS_INFO_STATE         pInfoState,
    PGET_INFO_CLASS_DISPATCH_ENTRY  pInfoEntry
    )
{
    NTSTATUS                       ntStatus     = 0;
    PSRV_EXEC_CONTEXT_SMB_V1       pCtxSmb1     = pExecContext->pProtocolContext;
    PSRV_TRANS2_STATE_SMB_V1       pTrans2State = pCtxSmb1->hState;
    PFILE_NETWORK_OPEN_INFORMATION pInfo        = NULL;

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

    pInfo = (PFILE_NETWORK_OPEN_INFORMATION)pInfoState->pData;
    if (!pInfo)
    {
        ntStatus = SrvAllocateMemory(sizeof(*pInfo), OUT_PPVOID(&pInfo));
        BAIL_ON_NT_STATUS(ntStatus);

        pInfoState->pData = (PBYTE)pInfo;
        pInfoState->ulDataLen = sizeof(*pInfo);

        if (IoRtlEcpListIsAcknowledged(
                pTrans2State->pEcpList,
                IO_ECP_TYPE_NET_OPEN_INFO))
        {
            *pInfo = *pTrans2State->pNetworkOpenInfo;

            pExecContext->ioStatusBlock.Status = STATUS_SUCCESS;
            pExecContext->ioStatusBlock.BytesTransferred = sizeof(*pInfo);
        }
        else
        {
            SrvPrepareExecContextAsync(pExecContext);

            ntStatus = IoQueryInformationFile(
                            pInfoState->hFile,
                            pExecContext->pAcb,
                            &pExecContext->ioStatusBlock,
                            pInfoState->pData,
                            pInfoState->ulDataLen,
                            FileNetworkOpenInformation);
            BAIL_ON_NT_STATUS(ntStatus);

            SrvReleaseExecContextAsync(pExecContext); // completed synchronously
        }
    }

error:

    return ntStatus;
}

static
NTSTATUS
SrvGetFullEas(
    PSRV_EXEC_CONTEXT               pExecContext,
    PSRV_PROCESS_INFO_STATE         pInfoState,
    PGET_INFO_CLASS_DISPATCH_ENTRY  pInfoEntry
    )
{
    NTSTATUS                   ntStatus     = 0;
    PSRV_EXEC_CONTEXT_SMB_V1   pCtxSmb1     = pExecContext->pProtocolContext;
    PSRV_TRANS2_STATE_SMB_V1   pTrans2State = pCtxSmb1->hState;
    PSMB_GEA_LIST              pPackedList  = (PSMB_GEA_LIST)pTrans2State->pData;
    PSMB_GEA                   pPackedGea   = NULL;
    USHORT                     packedSize   = pTrans2State->pRequestHeader->dataCount;
    ULONG                      getEaSize    = 0;
    PFILE_GET_EA_INFORMATION   pGetEaEntry  = NULL;
    ULONG                      nextOffset   = 0;
    USHORT                     usNewSize    = 0;

    if (pInfoState->usInfoClass == SMB_INFO_QUERY_EAS_FROM_LIST &&
        !pTrans2State->pEaRequest)
    {
        // Parse SMB_GEA_LIST to fix endianness, validate, and calculate
        // size of FILE_GET_EA_INFORMATION
        if (packedSize < sizeof(*pPackedList))
        {
            ntStatus = STATUS_INVALID_NETWORK_RESPONSE;
            BAIL_ON_NT_STATUS(ntStatus);
        }

        SMB_LTOH32_INPLACE(pPackedList->ListSize);
        if (pPackedList->ListSize > packedSize)
        {
            ntStatus = STATUS_INVALID_NETWORK_RESPONSE;
            BAIL_ON_NT_STATUS(ntStatus);
        }
        pPackedGea = (PSMB_GEA)pPackedList->List;
        while ((PBYTE) pPackedGea < pTrans2State->pData + pPackedList->ListSize)
        {
            if ((PBYTE)pPackedGea + sizeof(*pPackedGea) >
                pTrans2State->pData + pPackedList->ListSize ||
                &pPackedGea->AttrName[pPackedGea->AttrNameLen] >
                pTrans2State->pData + pPackedList->ListSize ||
                pPackedGea->AttrName[pPackedGea->AttrNameLen] != 0 ||
                strlen((char*)pPackedGea->AttrName) != pPackedGea->AttrNameLen)
            {
                ntStatus = STATUS_INVALID_NETWORK_RESPONSE;
                BAIL_ON_NT_STATUS(ntStatus);
            }

            getEaSize +=
                LW_FIELD_OFFSET(FILE_GET_EA_INFORMATION, EaName) +
                pPackedGea->AttrNameLen;

            // Round up to 4-byte boundary
            if (getEaSize % 4)
            {
                getEaSize += 4 - getEaSize % 4;
            }

            pPackedGea =
                (PSMB_GEA)&pPackedGea->AttrName[pPackedGea->AttrNameLen + 1];
        }

        // Allocate request list
        ntStatus = LW_RTL_ALLOCATE(
                        &pTrans2State->pEaRequest,
                        FILE_GET_EA_INFORMATION,
                        getEaSize);
        BAIL_ON_NT_STATUS(ntStatus);
        pTrans2State->EaRequestSize = getEaSize;

        // Unmarshal request list
        pGetEaEntry = pTrans2State->pEaRequest;
        pPackedGea = (PSMB_GEA) pPackedList->List;
        while ((PBYTE) pPackedGea < pTrans2State->pData + pPackedList->ListSize)
        {
            pGetEaEntry->EaNameLength = pPackedGea->AttrNameLen;
            memcpy(
                pGetEaEntry->EaName,
                pPackedGea->AttrName,
                pPackedGea->AttrNameLen + 1);

            pPackedGea =
                (PSMB_GEA)&pPackedGea->AttrName[pPackedGea->AttrNameLen + 1];

            if ((PBYTE)pPackedGea < pTrans2State->pData + pPackedList->ListSize)
            {
                nextOffset =
                    LW_FIELD_OFFSET(FILE_GET_EA_INFORMATION, EaName) +
                    pGetEaEntry->EaNameLength;

                if (nextOffset % 4)
                {
                    nextOffset += 4 - nextOffset % 4;
                }
            }
            else
            {
                nextOffset = 0;
            }

            pGetEaEntry->NextEntryOffset = nextOffset;
            pGetEaEntry =
                (PFILE_GET_EA_INFORMATION)((PBYTE)pGetEaEntry + nextOffset);
        }
    }

    ntStatus = pExecContext->ioStatusBlock.Status;
    for (;;)
    {
        ULONG ulNewSize = 0;

        if (!pInfoState->ulDataLen)
        {
            usNewSize = sizeof(FILE_FULL_EA_INFORMATION) * 8;
        }
        else if (ntStatus == STATUS_SUCCESS)
        {
            break;
        }
        else if (ntStatus != STATUS_BUFFER_OVERFLOW &&
                 ntStatus != STATUS_BUFFER_TOO_SMALL)
        {
            BAIL_ON_NT_STATUS(ntStatus);
        }
        else
        {
            ulNewSize = pInfoState->ulDataLen * 2;
        }

        ntStatus = SrvReallocMemory(
                        pInfoState->pData,
                        ulNewSize,
                        OUT_PPVOID(&pInfoState->pData));
        BAIL_ON_NT_STATUS(ntStatus);

        pInfoState->ulDataLen = ulNewSize;

        SrvPrepareExecContextAsync(pExecContext);

        ntStatus = IoQueryEaFile(
                        pInfoState->hFile,
                        pExecContext->pAcb,
                        &pExecContext->ioStatusBlock,
                        (PFILE_FULL_EA_INFORMATION)pInfoState->pData,
                        pInfoState->ulDataLen,
                        FALSE,
                        pTrans2State->pEaRequest,
                        pTrans2State->EaRequestSize,
                        NULL, /* FIXME: use this index */
                        TRUE);
        if (ntStatus != STATUS_PENDING)
        {
            // synchronous completion
            SrvReleaseExecContextAsync(pExecContext);
        }
    }

    BAIL_ON_NT_STATUS(ntStatus);

cleanup:

    if (ntStatus == STATUS_SUCCESS || ntStatus == STATUS_BUFFER_OVERFLOW)
    {
        pInfoState->ulActualDataLen =
            pExecContext->ioStatusBlock.BytesTransferred;
        ntStatus = STATUS_SUCCESS;
    }

    return ntStatus;

error:

    goto cleanup;
}

NTSTATUS
SrvMarshalFileBasicInfo_SMB_V1(
    IN PSRV_EXEC_CONTEXT pExecContext,
    IN PSRV_PROCESS_INFO_STATE pInfoState,
    IN PGET_INFO_CLASS_DISPATCH_ENTRY pInfoEntry,
    IN OUT PBYTE pOutBuffer,
    IN OUT ULONG ulBytesAvailable,
    IN OUT PULONG pulBytesUsed
    )
{
    PFILE_BASIC_INFORMATION        pFileBasicInfo       = NULL;
    PTRANS2_FILE_BASIC_INFORMATION pTrans2FileBasicInfo = NULL;

    pFileBasicInfo       = (PFILE_BASIC_INFORMATION)pInfoState->pData;
    pTrans2FileBasicInfo = (PTRANS2_FILE_BASIC_INFORMATION)pOutBuffer;

    pTrans2FileBasicInfo->CreationTime   = pFileBasicInfo->CreationTime;
    pTrans2FileBasicInfo->LastAccessTime = pFileBasicInfo->LastAccessTime;
    pTrans2FileBasicInfo->LastWriteTime  = pFileBasicInfo->LastWriteTime;
    pTrans2FileBasicInfo->ChangeTime     = pFileBasicInfo->ChangeTime;
    pTrans2FileBasicInfo->FileAttributes = pFileBasicInfo->FileAttributes;
    pTrans2FileBasicInfo->unknown        = 0;

    *pulBytesUsed = pInfoEntry->ProtoStructSize;

    return STATUS_SUCCESS;
}

static
NTSTATUS
SrvMarshalFileStandardInfo_SMB_V1(
    IN PSRV_EXEC_CONTEXT pExecContext,
    IN PSRV_PROCESS_INFO_STATE pInfoState,
    IN PGET_INFO_CLASS_DISPATCH_ENTRY pInfoEntry,
    IN OUT PBYTE pOutBuffer,
    IN OUT ULONG ulBytesAvailable,
    IN OUT PULONG pulBytesUsed
    )
{
    PFILE_STANDARD_INFORMATION        pFileStandardInfo       = NULL;
    PTRANS2_FILE_STANDARD_INFORMATION pTrans2FileStandardInfo = NULL;

    pFileStandardInfo       = (PFILE_STANDARD_INFORMATION)pInfoState->pData;
    pTrans2FileStandardInfo = (PTRANS2_FILE_STANDARD_INFORMATION)pOutBuffer;

    pTrans2FileStandardInfo->AllocationSize = pFileStandardInfo->AllocationSize;
    pTrans2FileStandardInfo->EndOfFile      = pFileStandardInfo->EndOfFile;
    pTrans2FileStandardInfo->NumberOfLinks  = pFileStandardInfo->NumberOfLinks;
    pTrans2FileStandardInfo->bDeletePending = pFileStandardInfo->DeletePending;
    pTrans2FileStandardInfo->bDirectory     = pFileStandardInfo->Directory;
    pTrans2FileStandardInfo->pad            = 0;

    *pulBytesUsed = pInfoEntry->ProtoStructSize;

    return STATUS_SUCCESS;
}

static
NTSTATUS
SrvMarshalFileEaInfo_SMB_V1(
    IN PSRV_EXEC_CONTEXT pExecContext,
    IN PSRV_PROCESS_INFO_STATE pInfoState,
    IN PGET_INFO_CLASS_DISPATCH_ENTRY pInfoEntry,
    IN OUT PBYTE pOutBuffer,
    IN OUT ULONG ulBytesAvailable,
    IN OUT PULONG pulBytesUsed
    )
{
    PFILE_EA_INFORMATION        pFileEAInfo       = NULL;
    PTRANS2_FILE_EA_INFORMATION pTrans2FileEAInfo = NULL;

    pFileEAInfo       = (PFILE_EA_INFORMATION)pInfoState->pData;
    pTrans2FileEAInfo = (PTRANS2_FILE_EA_INFORMATION)pOutBuffer;

    pTrans2FileEAInfo->EaSize = pFileEAInfo->EaSize;

    *pulBytesUsed = pInfoEntry->ProtoStructSize;

    return STATUS_SUCCESS;
}

static
NTSTATUS
SrvMarshalFileStreamInfo_SMB_V1(
    IN PSRV_EXEC_CONTEXT pExecContext,
    IN PSRV_PROCESS_INFO_STATE pInfoState,
    IN PGET_INFO_CLASS_DISPATCH_ENTRY pInfoEntry,
    IN OUT PBYTE pOutBuffer,
    IN OUT ULONG ulBytesAvailable,
    IN OUT PULONG pulBytesUsed
    )
{
    PFILE_STREAM_INFORMATION     pFileStreamInfo    = NULL;
    PSMB_FILE_STREAM_INFO_HEADER pSMBFileStreamInfo = NULL;
    ULONG                        ulIoBytesAvailable = 0;

    *pulBytesUsed       = 0;
    pFileStreamInfo     = (PFILE_STREAM_INFORMATION)pInfoState->pData;
    ulIoBytesAvailable  = pInfoState->ulActualDataLen;

    while (ulIoBytesAvailable > sizeof(*pFileStreamInfo))
    {
        ULONG  ulInfoBytesRequired = 0;
        USHORT usAlign             = 0;

        ulInfoBytesRequired += sizeof(*pSMBFileStreamInfo);
        ulInfoBytesRequired += pFileStreamInfo->StreamNameLength;

        /* Null terminate all stream names but the last. */
        if (pFileStreamInfo->NextEntryOffset != 0)
        {
            ulInfoBytesRequired += sizeof(wchar16_t);
            usAlign             += sizeof(wchar16_t);

            if (ulInfoBytesRequired % 8)
            {
                ulInfoBytesRequired += 8 - (ulInfoBytesRequired % 8);
                usAlign             += 8 - (ulInfoBytesRequired % 8);
            }
        }

        if (ulBytesAvailable < ulInfoBytesRequired)
        {
            break;
        }

        pSMBFileStreamInfo = (PSMB_FILE_STREAM_INFO_HEADER)pOutBuffer;
        pSMBFileStreamInfo->ulNextEntryOffset = ulInfoBytesRequired;
        pSMBFileStreamInfo->llStreamAllocationSize =
            pFileStreamInfo->StreamAllocationSize;
        pSMBFileStreamInfo->llStreamSize = pFileStreamInfo->StreamSize;
        pSMBFileStreamInfo->ulStreamNameLength =
            pFileStreamInfo->StreamNameLength;

        pOutBuffer = (PBYTE)pSMBFileStreamInfo->StreamName;
        memcpy(
            pOutBuffer,
            pFileStreamInfo->StreamName,
            pSMBFileStreamInfo->ulStreamNameLength);
        pOutBuffer += pSMBFileStreamInfo->ulStreamNameLength;

        memset(pOutBuffer, 0, usAlign);
        pOutBuffer += usAlign;

        *pulBytesUsed += ulInfoBytesRequired;

        if (pFileStreamInfo->NextEntryOffset == 0)
        {
            break;
        }

        ulIoBytesAvailable -= pFileStreamInfo->NextEntryOffset;
        ulBytesAvailable   -= ulInfoBytesRequired;
        pFileStreamInfo = 
            (PFILE_STREAM_INFORMATION)
            ((PBYTE)pFileStreamInfo + pFileStreamInfo->NextEntryOffset);
    }

    if (pSMBFileStreamInfo)
    {
        pSMBFileStreamInfo->ulNextEntryOffset = 0;
    }

    return STATUS_SUCCESS;
}

static
NTSTATUS
SrvMarshalFileAllInfo_SMB_V1(
    IN PSRV_EXEC_CONTEXT pExecContext,
    IN PSRV_PROCESS_INFO_STATE pInfoState,
    IN PGET_INFO_CLASS_DISPATCH_ENTRY pInfoEntry,
    IN OUT PBYTE pOutBuffer,
    IN OUT ULONG ulBytesAvailable,
    IN OUT PULONG pulBytesUsed
    )
{
    NTSTATUS                     ntStatus           = STATUS_SUCCESS;
    PFILE_ALL_INFORMATION        pFileAllInfo       = NULL;
    PTRANS2_FILE_ALL_INFORMATION pTrans2FileAllInfo = NULL;
    ULONG                        ulFileNameLen      = 0;
    ULONG                        ulBytesNeeded      = 0;

    *pulBytesUsed      = 0;
    pFileAllInfo       = (PFILE_ALL_INFORMATION)pInfoState->pData;
    pTrans2FileAllInfo = (PTRANS2_FILE_ALL_INFORMATION)pOutBuffer;

    /* The file name is the dynamic portion of what the FS returned. */
    ulFileNameLen = pInfoState->ulActualDataLen - pInfoEntry->IOStructSize;
    ulBytesNeeded = pInfoEntry->ProtoStructSize + ulFileNameLen;

    LW_ASSERT(ulBytesAvailable >= ulBytesNeeded);

    pTrans2FileAllInfo->CreationTime   =
        pFileAllInfo->BasicInformation.CreationTime;
    pTrans2FileAllInfo->LastAccessTime =
        pFileAllInfo->BasicInformation.LastAccessTime;
    pTrans2FileAllInfo->LastWriteTime  =
        pFileAllInfo->BasicInformation.LastWriteTime;
    pTrans2FileAllInfo->ChangeTime     =
        pFileAllInfo->BasicInformation.ChangeTime;
    pTrans2FileAllInfo->FileAttributes =
        pFileAllInfo->BasicInformation.FileAttributes;
    pTrans2FileAllInfo->unknown        = 0;
    pTrans2FileAllInfo->AllocationSize =
        pFileAllInfo->StandardInformation.AllocationSize;
    pTrans2FileAllInfo->EndOfFile      =
        pFileAllInfo->StandardInformation.EndOfFile;
    pTrans2FileAllInfo->NumberOfLinks  =
        pFileAllInfo->StandardInformation.NumberOfLinks;
    pTrans2FileAllInfo->bDeletePending =
        pFileAllInfo->StandardInformation.DeletePending;
    pTrans2FileAllInfo->bDirectory     =
        pFileAllInfo->StandardInformation.Directory;
    pTrans2FileAllInfo->pad            = 0;
    pTrans2FileAllInfo->EaSize         = pFileAllInfo->EaInformation.EaSize;

    ntStatus = SrvMarshalFileName_SMB_V1(
                    /* Absolute Path */NULL,
                    pFileAllInfo->NameInformation.FileName,
                    &ulFileNameLen,
                    (PBYTE)&pTrans2FileAllInfo->FileName,
                    ulBytesAvailable - pInfoEntry->ProtoStructSize,
                    pulBytesUsed);
    BAIL_ON_NT_STATUS(ntStatus);

    pTrans2FileAllInfo->FileNameLength = ulFileNameLen;
    *pulBytesUsed += pInfoEntry->ProtoStructSize;

error:

    return ntStatus;
}

static
NTSTATUS
SrvMarshalFileNameInfo_SMB_V1(
    IN PSRV_EXEC_CONTEXT pExecContext,
    IN PSRV_PROCESS_INFO_STATE pInfoState,
    IN PGET_INFO_CLASS_DISPATCH_ENTRY pInfoEntry,
    IN OUT PBYTE pOutBuffer,
    IN OUT ULONG ulBytesAvailable,
    IN OUT PULONG pulBytesUsed
    )
{
    PSRV_EXEC_CONTEXT_SMB_V1      pCtxSmb1 = pExecContext->pProtocolContext;
    PTRANS2_FILE_NAME_INFORMATION pTrans2FileNameInfo = NULL;
    NTSTATUS                      ntStatus         = STATUS_SUCCESS;
    PLWIO_SRV_TREE                pTree            = NULL;
    PFILE_NAME_INFORMATION        pFileNameInfo    = NULL;
    ULONG                         ulFileNameLen    = 0;

    *pulBytesUsed       = 0;
    pFileNameInfo       = (PFILE_NAME_INFORMATION)pInfoState->pData;
    pTrans2FileNameInfo = (PTRANS2_FILE_NAME_INFORMATION)pOutBuffer;
    ulFileNameLen       = pFileNameInfo->FileNameLength;

    if (pInfoState->usInfoClass == FileAlternateNameInformation)
    {
        pTree = pCtxSmb1->pTree;
    }

    ntStatus = SrvMarshalFileName_SMB_V1(
                    pTree,
                    pFileNameInfo->FileName,
                    &ulFileNameLen,
                    (PBYTE)&pTrans2FileNameInfo->FileName,
                    ulBytesAvailable - pInfoEntry->ProtoStructSize,
                    pulBytesUsed);
    BAIL_ON_NT_STATUS(ntStatus);

    pTrans2FileNameInfo->ulFileNameLength = ulFileNameLen;
    *pulBytesUsed += pInfoEntry->ProtoStructSize;

error:

    return ntStatus;
}

static
NTSTATUS
SrvMarshalFileInternalInfo_SMB_V1(
    IN PSRV_EXEC_CONTEXT pExecContext,
    IN PSRV_PROCESS_INFO_STATE pInfoState,
    IN PGET_INFO_CLASS_DISPATCH_ENTRY pInfoEntry,
    IN OUT PBYTE pOutBuffer,
    IN OUT ULONG ulBytesAvailable,
    IN OUT PULONG pulBytesUsed
    )
{
    PFILE_INTERNAL_INFORMATION        pFileInternalInfo       = NULL;
    PTRANS2_FILE_INTERNAL_INFORMATION pTrans2FileInternalInfo = NULL;

    pFileInternalInfo       = (PFILE_INTERNAL_INFORMATION)pInfoState->pData;
    pTrans2FileInternalInfo = (PTRANS2_FILE_INTERNAL_INFORMATION)pOutBuffer;

    pTrans2FileInternalInfo->IndexNumber = pFileInternalInfo->IndexNumber;

    *pulBytesUsed = pInfoEntry->ProtoStructSize;

    return STATUS_SUCCESS;
}

static
NTSTATUS
SrvMarshalFileNetworkOpenInfo_SMB_V1(
    IN PSRV_EXEC_CONTEXT pExecContext,
    IN PSRV_PROCESS_INFO_STATE pInfoState,
    IN PGET_INFO_CLASS_DISPATCH_ENTRY pInfoEntry,
    IN OUT PBYTE pOutBuffer,
    IN OUT ULONG ulBytesAvailable,
    IN OUT PULONG pulBytesUsed
    )
{
    PFILE_NETWORK_OPEN_INFORMATION        pFileNetOpenInfo       = NULL;
    PTRANS2_FILE_NETWORK_OPEN_INFORMATION pTrans2FileNetOpenInfo = NULL;

    pFileNetOpenInfo       = (PFILE_NETWORK_OPEN_INFORMATION)pInfoState->pData;
    pTrans2FileNetOpenInfo = (PTRANS2_FILE_NETWORK_OPEN_INFORMATION)pOutBuffer;

    pTrans2FileNetOpenInfo->CreationTime    = pFileNetOpenInfo->CreationTime;
    pTrans2FileNetOpenInfo->LastAccessTime  = pFileNetOpenInfo->LastAccessTime;
    pTrans2FileNetOpenInfo->LastWriteTime   = pFileNetOpenInfo->LastWriteTime;
    pTrans2FileNetOpenInfo->ChangeTime      = pFileNetOpenInfo->ChangeTime;
    pTrans2FileNetOpenInfo->AllocationSize  = pFileNetOpenInfo->AllocationSize;
    pTrans2FileNetOpenInfo->EndOfFile       = pFileNetOpenInfo->EndOfFile;
    pTrans2FileNetOpenInfo->FileAttributes  = pFileNetOpenInfo->FileAttributes;
    pTrans2FileNetOpenInfo->pad             = 0;

    *pulBytesUsed = pInfoEntry->ProtoStructSize;

    return STATUS_SUCCESS;
}

static
NTSTATUS
SrvMarshalFileSmbStandardlInfo_SMB_V1(
    IN PSRV_EXEC_CONTEXT pExecContext,
    IN PSRV_PROCESS_INFO_STATE pInfoState,
    IN PGET_INFO_CLASS_DISPATCH_ENTRY pInfoEntry,
    IN OUT PBYTE pOutBuffer,
    IN OUT ULONG ulBytesAvailable,
    IN OUT PULONG pulBytesUsed
    )
{
    NTSTATUS                              ntStatus              = STATUS_SUCCESS;
    PFILE_ALL_INFORMATION                 pFileAllInfo          = NULL;
    PTRANS2_FILE_SMB_STANDARD_INFORMATION pTrans2FileSmbStdInfo = NULL;

    pFileAllInfo          = (PFILE_ALL_INFORMATION)pInfoState->pData;
    pTrans2FileSmbStdInfo = (PTRANS2_FILE_SMB_STANDARD_INFORMATION)pOutBuffer;

    ntStatus = WireNTTimeToSMBDateTime(
                 pFileAllInfo->BasicInformation.CreationTime,
		 &pTrans2FileSmbStdInfo->CreationDate,
		 &pTrans2FileSmbStdInfo->CreationTime);
    BAIL_ON_NT_STATUS(ntStatus);

    ntStatus = WireNTTimeToSMBDateTime(
                 pFileAllInfo->BasicInformation.LastAccessTime,
		 &pTrans2FileSmbStdInfo->LastAccessDate,
		 &pTrans2FileSmbStdInfo->LastAccessTime);
    BAIL_ON_NT_STATUS(ntStatus);

    ntStatus = WireNTTimeToSMBDateTime(
	    pFileAllInfo->BasicInformation.LastWriteTime,
		 &pTrans2FileSmbStdInfo->LastWriteDate,
		 &pTrans2FileSmbStdInfo->LastWriteTime);
    BAIL_ON_NT_STATUS(ntStatus);

    pTrans2FileSmbStdInfo->FileSize       =
        (ULONG)pFileAllInfo->StandardInformation.EndOfFile;
    pTrans2FileSmbStdInfo->AllocationSize =
        (ULONG)pFileAllInfo->StandardInformation.AllocationSize;
    pTrans2FileSmbStdInfo->Attributes     =
        (USHORT)pFileAllInfo->BasicInformation.FileAttributes;
    pTrans2FileSmbStdInfo->Attributes     =
        (USHORT)pFileAllInfo->BasicInformation.FileAttributes;

    *pulBytesUsed = pInfoEntry->ProtoStructSize;

error:

    return ntStatus;
}

static
NTSTATUS
SrvMarshalFileAccessInfo_SMB_V1(
    IN PSRV_EXEC_CONTEXT pExecContext,
    IN PSRV_PROCESS_INFO_STATE pInfoState,
    IN PGET_INFO_CLASS_DISPATCH_ENTRY pInfoEntry,
    IN OUT PBYTE pOutBuffer,
    IN OUT ULONG ulBytesAvailable,
    IN OUT PULONG pulBytesUsed
    )
{
    PFILE_ACCESS_INFORMATION        pFileAccessInfo    = NULL;
    PTRANS2_FILE_ACCESS_INFORMATION pTrans2FileAccessInfo = NULL;

    pFileAccessInfo       = (PFILE_ACCESS_INFORMATION)pInfoState->pData;
    pTrans2FileAccessInfo = (PTRANS2_FILE_ACCESS_INFORMATION)pOutBuffer;

    pTrans2FileAccessInfo->AccessMask = pFileAccessInfo->AccessFlags;

    *pulBytesUsed = pInfoEntry->ProtoStructSize;

    return STATUS_SUCCESS;
}

static
NTSTATUS
SrvMarshalFileAttributeTagInfo_SMB_V1(
    IN PSRV_EXEC_CONTEXT pExecContext,
    IN PSRV_PROCESS_INFO_STATE pInfoState,
    IN PGET_INFO_CLASS_DISPATCH_ENTRY pInfoEntry,
    IN OUT PBYTE pOutBuffer,
    IN OUT ULONG ulBytesAvailable,
    IN OUT PULONG pulBytesUsed
    )
{
    PFILE_ATTRIBUTE_TAG_INFORMATION        pFileAttrTagInfo       = NULL;
    PTRANS2_FILE_ATTRIBUTE_TAG_INFORMATION pTrans2FileAttrTagInfo = NULL;

    pFileAttrTagInfo     = (PFILE_ATTRIBUTE_TAG_INFORMATION)pInfoState->pData;
    pTrans2FileAttrTagInfo = (PTRANS2_FILE_ATTRIBUTE_TAG_INFORMATION)pOutBuffer;

    pTrans2FileAttrTagInfo->FileAttributes = pFileAttrTagInfo->FileAttributes;
    pTrans2FileAttrTagInfo->ReparseTag     = pFileAttrTagInfo->ReparseTag;

    *pulBytesUsed = pInfoEntry->ProtoStructSize;

    return STATUS_SUCCESS;
}

static
NTSTATUS
SrvMarshalFileEaSize_SMB_V1(
    IN PSRV_EXEC_CONTEXT pExecContext,
    IN PSRV_PROCESS_INFO_STATE pInfoState,
    IN PGET_INFO_CLASS_DISPATCH_ENTRY pInfoEntry,
    IN OUT PBYTE pOutBuffer,
    IN OUT ULONG ulBytesAvailable,
    IN OUT PULONG pulBytesUsed
    )
{
    NTSTATUS                 ntStatus              = STATUS_SUCCESS;
    PFILE_ALL_INFORMATION    pFileAllInfo          = NULL;
    PTRANS2_FILE_SMB_EA_SIZE pTrans2FileEaSizeInfo = NULL;

    *pulBytesUsed         = 0;
    pFileAllInfo          = (PFILE_ALL_INFORMATION)pInfoState->pData;
    pTrans2FileEaSizeInfo = (PTRANS2_FILE_SMB_EA_SIZE)pOutBuffer;

    ntStatus = WireNTTimeToSMBDateTime(
        pFileAllInfo->BasicInformation.CreationTime,
        &pTrans2FileEaSizeInfo->CreationDate,
        &pTrans2FileEaSizeInfo->CreationTime);
    BAIL_ON_NT_STATUS(ntStatus);

    ntStatus = WireNTTimeToSMBDateTime(
        pFileAllInfo->BasicInformation.LastAccessTime,
        &pTrans2FileEaSizeInfo->LastAccessDate,
        &pTrans2FileEaSizeInfo->LastAccessTime);
    BAIL_ON_NT_STATUS(ntStatus);

    ntStatus = WireNTTimeToSMBDateTime(
        pFileAllInfo->BasicInformation.LastWriteTime,
        &pTrans2FileEaSizeInfo->LastWriteDate,
        &pTrans2FileEaSizeInfo->LastWriteTime);
    BAIL_ON_NT_STATUS(ntStatus);

    pTrans2FileEaSizeInfo->FileSize =
        (ULONG)pFileAllInfo->StandardInformation.EndOfFile;
    pTrans2FileEaSizeInfo->AllocationSize =
        (ULONG)pFileAllInfo->StandardInformation.AllocationSize;
    pTrans2FileEaSizeInfo->Attributes =
        SMB_FILE_ATTRIBUTES_FROM_NATIVE(pFileAllInfo->BasicInformation.FileAttributes);
    pTrans2FileEaSizeInfo->EaSize =
        (USHORT)pFileAllInfo->EaInformation.EaSize;

    *pulBytesUsed = pInfoEntry->ProtoStructSize;

error:

    return STATUS_SUCCESS;
}

static
NTSTATUS
SrvMarshalEas_SMB_V1(
    IN PSRV_EXEC_CONTEXT pExecContext,
    IN PSRV_PROCESS_INFO_STATE pInfoState,
    IN PGET_INFO_CLASS_DISPATCH_ENTRY pInfoEntry,
    IN OUT PBYTE pOutBuffer,
    IN OUT ULONG ulBytesAvailable,
    IN OUT PULONG pulBytesUsed
    )
{
    NTSTATUS                  status      = STATUS_SUCCESS;
    PBYTE                     pDataCursor = pOutBuffer;
    PBYTE                     pData       = NULL;
    PFILE_FULL_EA_INFORMATION pFea        = NULL;
    PSMB_FEA_LIST             pPackedList = NULL;
    PSMB_FEA                  pPackedFea  = NULL;

    pFea = (PFILE_FULL_EA_INFORMATION)pInfoState->pData;

    pPackedList = (PSMB_FEA_LIST)pOutBuffer;
    status = Advance(&pDataCursor, &ulBytesAvailable, sizeof(*pPackedList));
    BAIL_ON_NT_STATUS(status);

    while (pFea)
    {
        pPackedFea = (PSMB_FEA)pDataCursor;
        status = Advance(&pDataCursor, &ulBytesAvailable, sizeof(*pPackedFea));
        BAIL_ON_NT_STATUS(status);

        pPackedFea->Flag = pFea->Flags;
        pPackedFea->AttrNameLen = pFea->EaNameLength;
        pPackedFea->AttrValueLen = SMB_HTOL16(pFea->EaValueLength);

        pData = pDataCursor;
        status = Advance(&pDataCursor, &ulBytesAvailable, pFea->EaNameLength+1);
        BAIL_ON_NT_STATUS(status);
        memcpy(pData, pFea->EaName, pFea->EaNameLength + 1);

        pData = pDataCursor;
        status = Advance(&pDataCursor, &ulBytesAvailable, pFea->EaValueLength);
        BAIL_ON_NT_STATUS(status);
        memcpy(pData, &pFea->EaName[pFea->EaNameLength+1], pFea->EaValueLength);

        pFea = pFea->NextEntryOffset ?
            (PFILE_FULL_EA_INFORMATION) ((PBYTE) pFea + pFea->NextEntryOffset) :
            NULL;
    }

    *pulBytesUsed = pDataCursor - pOutBuffer;
    pPackedList->ListSize = SMB_HTOL32(*pulBytesUsed);

error:

    return status;
}

NTSTATUS
SrvQueryInfo(
    PSRV_EXEC_CONTEXT pExecContext
    )
{
    NTSTATUS                   ntStatus     = 0;
    PSRV_EXEC_CONTEXT_SMB_V1   pCtxSmb1     = pExecContext->pProtocolContext;
    PSRV_TRANS2_STATE_SMB_V1   pTrans2State = pCtxSmb1->hState;

    ntStatus = SrvGetTableInfo(
                    pExecContext,
                    &pTrans2State->infoState,
                    Trans2InfoClassDispatchTable,
                    LW_ARRAY_SIZE(Trans2InfoClassDispatchTable));

    return ntStatus;
}

NTSTATUS
SrvBuildQueryInfoResponse(
    PSRV_EXEC_CONTEXT pExecContext
    )
{
    NTSTATUS                   ntStatus     = STATUS_SUCCESS;
    PLWIO_SRV_CONNECTION       pConnection  = pExecContext->pConnection;
    PSRV_EXEC_CONTEXT_SMB_V1   pCtxSmb1     = pExecContext->pProtocolContext;
    PSRV_TRANS2_STATE_SMB_V1   pTrans2State = pCtxSmb1->hState;
    PSRV_PROCESS_INFO_STATE    pInfoState   = &pTrans2State->infoState;
    ULONG                      iMsg         = pCtxSmb1->iMsg;
    PSRV_MESSAGE_SMB_V1        pSmbRequest  = &pCtxSmb1->pRequests[iMsg];
    PSRV_MESSAGE_SMB_V1        pSmbResponse = &pCtxSmb1->pResponses[iMsg];
    PTRANSACTION_SECONDARY_RESPONSE_HEADER pResponseHeader = NULL;
    PBYTE   pDataCursor         = pSmbResponse->pBuffer;
    PBYTE   pDataByteCountField = NULL;
    ULONG   ulBytesAvailable    = pSmbResponse->ulBytesAvailable;
    ULONG   ulOffset            = 0;
    ULONG   ulBytesUsed         = 0;
    ULONG   ulTotalBytesUsed    = 0;
    USHORT  usParam             = 0;
    PUSHORT pSetup              = NULL;
    BYTE    setupCount          = 0;

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

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

    ntStatus = WireMarshalTrans2ResponseSetup(
                    pDataCursor,
                    ulBytesAvailable,
                    ulOffset,
                    pSetup,
                    setupCount,
                    (PBYTE)&usParam,
                    sizeof(usParam),
                    &pResponseHeader,
                    pSmbResponse->pWordCount,
                    &pDataByteCountField,
                    &ulBytesUsed);
    BAIL_ON_NT_STATUS(ntStatus);

    pDataCursor      += ulBytesUsed;
    ulBytesAvailable -= ulBytesUsed;
    ulOffset         += ulBytesUsed;

    ntStatus = SrvMarshalTableInfo(
                    pExecContext,
                    pInfoState,
                    Trans2InfoClassDispatchTable,
                    LW_ARRAY_SIZE(Trans2InfoClassDispatchTable),
                    pDataCursor,
                    ulBytesAvailable,
                    &ulBytesUsed);
    BAIL_ON_NT_STATUS(ntStatus);

    pDataCursor      += ulBytesUsed;
    ulBytesAvailable -= ulBytesUsed;
    ulOffset         += ulBytesUsed;

    WireMarshalTrans2ResponseFini(
        pResponseHeader,
        pDataCursor,
        pDataByteCountField,
        ulBytesUsed);

    pSmbResponse->ulMessageSize = pDataCursor - pSmbResponse->pBuffer;

cleanup:

    return ntStatus;

error:

    pSmbResponse->pHeader = NULL;
    pSmbResponse->pAndXHeader = NULL;
    pSmbResponse->ulMessageSize = 0;

    goto cleanup;
}

static
NTSTATUS
SrvMarshalFileName_SMB_V1(
    PLWIO_SRV_TREE pTree,
    PWSTR pwszFileName,
    PULONG pulFileNameLength,
    PBYTE pOutBuffer,
    ULONG ulBytesAvailable,
    PULONG pulBytesUsed
    )
{
    NTSTATUS ntStatus       = STATUS_SUCCESS;
    PWSTR    pRawSharePath  = NULL;
    PWSTR    pwszTreePath   = NULL;
    ULONG    ulPrefixLength = 0;
    BOOLEAN  bShareInLock   = FALSE;

    if (pTree) {
        LWIO_LOCK_RWMUTEX_SHARED(bShareInLock, &pTree->pShareInfo->Mutex);

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

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

        // pwszTreePath is a reference to pRawSharePath, not a substring copy.
        ntStatus = SrvGetTreeRelativePath(pRawSharePath, &pwszTreePath);
        BAIL_ON_NT_STATUS(ntStatus);

        ntStatus = SrvMatchPathPrefix(
                        pwszFileName,
                        *pulFileNameLength/sizeof(wchar16_t),
                        pwszTreePath);

        if (STATUS_SUCCESS != ntStatus)
        {
            ntStatus = STATUS_INTERNAL_ERROR;
            BAIL_ON_NT_STATUS(ntStatus);
        }

        ulPrefixLength      = wc16slen(pwszTreePath) * sizeof(wchar16_t);
        *pulFileNameLength -= ulPrefixLength;
    }

    *pulBytesUsed = SMB_MIN(*pulFileNameLength, ulBytesAvailable);
    memcpy(pOutBuffer, (PBYTE)pwszFileName + ulPrefixLength, *pulBytesUsed);

cleanup:

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

    RTL_FREE(&pRawSharePath);

    return ntStatus;

error:

    goto cleanup;
}
