/* -*- 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 GET_INFO_CLASS_MARSHAL_FUNC SrvMarshalFileSystemAllocationInfo_SMB_V1;
static GET_INFO_CLASS_MARSHAL_FUNC SrvMarshalVolumeInfo_SMB_V1;
static GET_INFO_CLASS_MARSHAL_FUNC SrvMarshalFileSystemQuotaInfo_SMB_V1;

static GET_INFO_CLASS_DISPATCH_ENTRY Trans2FsInfoClassDispatchTable[] =
{
    [SMB_INFO_ALLOCATION]               =
    {
        SrvGetFileSystemInfoGeneric,
        SrvMarshalFileSystemAllocationInfo_SMB_V1,
        FileFsSizeInformation,
        sizeof(SMB_FS_INFO_ALLOCATION),
        sizeof(FILE_FS_SIZE_INFORMATION)
    },

    [SMB_INFO_VOLUME]                   =
    {
        SrvGetFileSystemInfoGeneric_WithString,
        SrvMarshalVolumeInfo_SMB_V1,
        FileFsVolumeInformation,
        sizeof(SMB1_FS_VOLUME_INFO_HEADER),
        sizeof(FILE_FS_VOLUME_INFORMATION)
    },

    [SMB_QUERY_FS_VOLUME_INFO]          =
    {
        SrvGetFileSystemInfoGeneric_WithString,
        SrvMarshalFileSystemVolumeInfo,
        FileFsVolumeInformation,
        sizeof(SMB_FS_VOLUME_INFO_HEADER),
        sizeof(FILE_FS_VOLUME_INFORMATION)
    },
    [SMB_QUERY_FS_VOLUME_INFO_ALIAS]    =
    {
        SrvGetFileSystemInfoGeneric_WithString,
        SrvMarshalFileSystemVolumeInfo,
        FileFsVolumeInformation,
        sizeof(SMB_FS_VOLUME_INFO_HEADER),
        sizeof(FILE_FS_VOLUME_INFORMATION)
    },

    [SMB_QUERY_FS_SIZE_INFO]            =
    {
        SrvGetFileSystemInfoGeneric,
        SrvMarshalFileSystemSizeInfo,
        FileFsSizeInformation,
        sizeof(SMB_FS_SIZE_INFO),
        sizeof(FILE_FS_SIZE_INFORMATION)
    },
    [SMB_QUERY_FS_SIZE_INFO_ALIAS]      =
    {
        SrvGetFileSystemInfoGeneric,
        SrvMarshalFileSystemSizeInfo,
        FileFsSizeInformation,
        sizeof(SMB_FS_SIZE_INFO),
        sizeof(FILE_FS_SIZE_INFORMATION)
    },

    [SMB_QUERY_FS_ATTRIBUTE_INFO]       =
    {
        SrvGetFileSystemInfoGeneric_WithString,
        SrvMarshalFileSystemAttributeInfo,
        FileFsAttributeInformation,
        sizeof(SMB_FS_ATTRIBUTE_INFO_HEADER),
        sizeof(FILE_FS_ATTRIBUTE_INFORMATION)
    },
    [SMB_QUERY_FS_ATTRIBUTE_INFO_ALIAS] =
    {
        SrvGetFileSystemInfoGeneric_WithString,
        SrvMarshalFileSystemAttributeInfo,
        FileFsAttributeInformation,
        sizeof(SMB_FS_ATTRIBUTE_INFO_HEADER),
        sizeof(FILE_FS_ATTRIBUTE_INFORMATION)
    },

    [SMB_QUERY_FS_DEVICE_INFO]          =
    {
        SrvGetFileSystemInfoGeneric,
        SrvMarshalFileSystemDeviceInfo,
        FileFsDeviceInformation,
        sizeof(SMB_FS_DEVICE_INFO),
        sizeof(FILE_FS_DEVICE_INFORMATION)
    },
    [SMB_QUERY_FS_DEVICE_INFO_ALIAS]    =
    {
        SrvGetFileSystemInfoGeneric,
        SrvMarshalFileSystemDeviceInfo,
        FileFsDeviceInformation,
        sizeof(SMB_FS_DEVICE_INFO),
        sizeof(FILE_FS_DEVICE_INFORMATION)
    },

    [SMB_QUERY_FS_FULL_SIZE_INFO]       =
    {
        SrvGetFileSystemInfoGeneric,
        SrvMarshalFileSystemFullSizeInfo,
        FileFsFullSizeInformation,
        sizeof(SMB_FS_FULL_SIZE_INFO),
        sizeof(FILE_FS_SIZE_INFORMATION)
    },

    [SMB_QUERY_FS_QUOTA_INFO]           =
    {
        SrvGetFileSystemInfoGeneric,
        SrvMarshalFileSystemQuotaInfo_SMB_V1,
        FileFsControlInformation,
        sizeof(TRANS2_FILE_FS_CONTROL_INFORMATION),
        sizeof(FILE_FS_CONTROL_INFORMATION)
    },

    [SMB_QUERY_CIFS_UNIX_INFO]          =
    {
        SrvGetUnsupportedInfo,
        SrvMarshalUnsupportedInfo,
        0,
        0,
        0,
    },
    [SMB_QUERY_MAC_FS_INFO]             =
    {
        SrvGetUnsupportedInfo,
        SrvMarshalUnsupportedInfo,
        0,
        0,
        0,
    }
};

static
NTSTATUS
SrvMarshalFileSystemAllocationInfo_SMB_V1(
    PSRV_EXEC_CONTEXT               pExecContext,
    PSRV_PROCESS_INFO_STATE         pInfoState,
    PGET_INFO_CLASS_DISPATCH_ENTRY  pInfoEntry,
    PBYTE                           pOutBuffer,
    ULONG                           ulBytesAvailable,
    PULONG                          pulBytesUsed
    )
{
    PFILE_FS_SIZE_INFORMATION pFSSizeInfo  = NULL;
    PSMB_FS_INFO_ALLOCATION   pFSAllocInfo = NULL;

    pFSSizeInfo  = (PFILE_FS_SIZE_INFORMATION)pInfoState->pData;
    pFSAllocInfo = (PSMB_FS_INFO_ALLOCATION)pOutBuffer;

    pFSAllocInfo->ulFileSystemId = 0;
    pFSAllocInfo->ulNumAllocationUnits =
        SMB_MIN(UINT32_MAX, pFSSizeInfo->TotalAllocationUnits);
    pFSAllocInfo->ulNumSectorsPerAllocationUnit =
        pFSSizeInfo->SectorsPerAllocationUnit;
    pFSAllocInfo->ulNumUnitsAvailable =
        SMB_MIN(UINT32_MAX, pFSSizeInfo->AvailableAllocationUnits);
    pFSAllocInfo->usNumBytesPerSector =
    SMB_MIN(UINT16_MAX, pFSSizeInfo->BytesPerSector);

    *pulBytesUsed = sizeof(*pFSAllocInfo);

    return STATUS_SUCCESS;
}

static
NTSTATUS
SrvMarshalVolumeInfo_SMB_V1(
    PSRV_EXEC_CONTEXT               pExecContext,
    PSRV_PROCESS_INFO_STATE         pInfoState,
    PGET_INFO_CLASS_DISPATCH_ENTRY  pInfoEntry,
    PBYTE                           pOutBuffer,
    ULONG                           ulBytesAvailable,
    PULONG                          pulBytesUsed
    )
{
    PFILE_FS_VOLUME_INFORMATION pFSVolInfo = NULL;
    PSMB1_FS_VOLUME_INFO_HEADER pFSInfoVolHeader = NULL;
    ULONG                       ulVolLabelLen = 0;
    ULONG                       ulBytesNeeded = 0;

    pFSVolInfo       = (PFILE_FS_VOLUME_INFORMATION)pInfoState->pData;
    pFSInfoVolHeader = (PSMB1_FS_VOLUME_INFO_HEADER)pOutBuffer;

    /* The Volume label is the dynamic portion of what the FS returned. */
    ulVolLabelLen = pInfoState->ulActualDataLen - pInfoEntry->IOStructSize;
    ulVolLabelLen = SMB_MIN(UINT8_MAX, ulVolLabelLen);
    ulBytesNeeded = pInfoEntry->ProtoStructSize + ulVolLabelLen;

    LW_ASSERT(ulBytesAvailable >= ulBytesNeeded);

    pFSInfoVolHeader->ulVolumeSerialNumber = pFSVolInfo->VolumeSerialNumber;
    pFSInfoVolHeader->ucNumLabelChars      = ulVolLabelLen & 0xFF;
    memcpy(
        pFSInfoVolHeader->wszVolumeLabel,
        pFSVolInfo->VolumeLabel,
        ulVolLabelLen);

    *pulBytesUsed = ulBytesNeeded;

    return STATUS_SUCCESS;
}

static
NTSTATUS
SrvMarshalFileSystemQuotaInfo_SMB_V1(
    PSRV_EXEC_CONTEXT               pExecContext,
    PSRV_PROCESS_INFO_STATE         pInfoState,
    PGET_INFO_CLASS_DISPATCH_ENTRY  pInfoEntry,
    PBYTE                           pOutBuffer,
    ULONG                           ulBytesAvailable,
    PULONG                          pulBytesUsed
    )
{
    PFILE_FS_CONTROL_INFORMATION        pFSCtlInfo       = NULL;
    PTRANS2_FILE_FS_CONTROL_INFORMATION pTrans2FSCtlInfo = NULL;

    pFSCtlInfo       = (PFILE_FS_CONTROL_INFORMATION)pInfoState->pData;
    pTrans2FSCtlInfo = (PTRANS2_FILE_FS_CONTROL_INFORMATION)pOutBuffer;

    pTrans2FSCtlInfo->FreeSpaceStartFiltering =
        pFSCtlInfo->FreeSpaceStartFiltering;
    pTrans2FSCtlInfo->FreeSpaceThreshold =
        pFSCtlInfo->FreeSpaceThreshold;
    pTrans2FSCtlInfo->FreeSpaceStopFiltering =
        pFSCtlInfo->FreeSpaceStopFiltering;
    pTrans2FSCtlInfo->DefaultQuotaThreshold =
        pFSCtlInfo->DefaultQuotaThreshold;
    pTrans2FSCtlInfo->DefaultQuotaLimit =
        pFSCtlInfo->DefaultQuotaLimit;
    pTrans2FSCtlInfo->FileSystemControlFlags =
        pFSCtlInfo->FileSystemControlFlags;

    *pulBytesUsed = pInfoEntry->ProtoStructSize;

    return STATUS_SUCCESS;
}

static
NTSTATUS
SrvQueryFilesystemInfo(
    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,
                    Trans2FsInfoClassDispatchTable,
                    LW_ARRAY_SIZE(Trans2FsInfoClassDispatchTable));

    return ntStatus;
}

static
NTSTATUS
SrvBuildQueryFilesystemInfoResponse(
    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;
    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,
                    NULL,
                    0,
                    &pResponseHeader,
                    pSmbResponse->pWordCount,
                    &pDataByteCountField,
                    &ulBytesUsed);
    BAIL_ON_NT_STATUS(ntStatus);

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

    ntStatus = SrvMarshalTableInfo(
                    pExecContext,
                    pInfoState,
                    Trans2FsInfoClassDispatchTable,
                    LW_ARRAY_SIZE(Trans2FsInfoClassDispatchTable),
                    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;
}

NTSTATUS
SrvProcessTrans2QueryFilesystemInformation(
    PSRV_EXEC_CONTEXT       pExecContext
    )
{
    NTSTATUS                   ntStatus     = 0;
    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];

    switch (pTrans2State->stage)
    {
        case SRV_TRANS2_STAGE_SMB_V1_INITIAL:

            if ((pTrans2State->pRequestHeader->parameterCount != 2) &&
                (pTrans2State->pRequestHeader->parameterCount != 4))
            {
                ntStatus = STATUS_INVALID_NETWORK_RESPONSE;
                BAIL_ON_NT_STATUS(ntStatus);
            }

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

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

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

            ntStatus = SrvBuildTreeRelativePath(
                            pTrans2State->pTree,
                            NULL,
                            &pTrans2State->fileName);
            BAIL_ON_NT_STATUS(ntStatus);

            pTrans2State->stage = SRV_TRANS2_STAGE_SMB_V1_CREATE_FILE_COMPLETED;

            SrvPrepareExecContextAsync(pExecContext);

            ntStatus = SrvIoCreateFile(
                            pExecContext,
                            pTrans2State->pTree->pShareInfo,
                            &pTrans2State->hFile,
                            pExecContext->pAcb,
                            &pExecContext->ioStatusBlock,
                            pTrans2State->pSession->pIoSecurityContext,
                            &pTrans2State->fileName,
                            pTrans2State->pSecurityDescriptor,
                            pTrans2State->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, // no snapshot since this is share root
                            &pTrans2State->pEcpList
                            );
            BAIL_ON_NT_STATUS(ntStatus);

            SrvReleaseExecContextAsync(pExecContext); // completed sync

            // intentional fall through

        case SRV_TRANS2_STAGE_SMB_V1_CREATE_FILE_COMPLETED:

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

            pInfoState->hFile       = pTrans2State->hFile;
            pInfoState->usInfoClass = *((PUSHORT)pTrans2State->pParameters);

            pTrans2State->stage = SRV_TRANS2_STAGE_SMB_V1_ATTEMPT_IO;

            // intentional fall through

        case SRV_TRANS2_STAGE_SMB_V1_ATTEMPT_IO:

            ntStatus = SrvQueryFilesystemInfo(pExecContext);
            BAIL_ON_NT_STATUS(ntStatus);

            pTrans2State->stage = SRV_TRANS2_STAGE_SMB_V1_BUILD_RESPONSE;

            // intentional fall through

        case SRV_TRANS2_STAGE_SMB_V1_BUILD_RESPONSE:

            ntStatus = SrvBuildQueryFilesystemInfoResponse(pExecContext);
            BAIL_ON_NT_STATUS(ntStatus);

            pTrans2State->stage = SRV_TRANS2_STAGE_SMB_V1_DONE;

            // intentional fall through

        case SRV_TRANS2_STAGE_SMB_V1_DONE:

            break;

        default:

            ntStatus = STATUS_INTERNAL_ERROR;
            BAIL_ON_NT_STATUS(ntStatus);

            break;
    }

cleanup:

    return ntStatus;

error:

    goto cleanup;
}
