/* -*- 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 Spectra Logic Corporation
 * 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) Spectra Logic Corporation. All rights reserved.
 *
 * Module Name:
 *
 *        processinfo.c
 *
 * Abstract:
 *
 *        Likewise IO (LWIO) - SRV
 *
 *        Common support for processing file/volume information requests.
 */

#include "includes.h"

NTSTATUS
SrvInitProcessInfoState(
    PSRV_PROCESS_INFO_STATE pInfoState,
    IO_FILE_HANDLE          hFile,
    ULONG                   ulOutputBufferLen,
    USHORT                  usInfoClass
    )
{
    pInfoState->hFile = hFile;
    pInfoState->ulOutputBufferLen = ulOutputBufferLen;
    pInfoState->usInfoClass = usInfoClass;

    return STATUS_SUCCESS;
}

void
SrvFiniProcessInfoState(
    PSRV_PROCESS_INFO_STATE pInfoState
    )
{
    if (pInfoState->pData)
    {
        SrvFreeMemory(pInfoState->pData);
    }
}

NTSTATUS
SrvMarshalUnsupportedInfo(
    PSRV_EXEC_CONTEXT               pExecContext,
    PSRV_PROCESS_INFO_STATE         pInfoState,
    PGET_INFO_CLASS_DISPATCH_ENTRY  pInfoEntry,
    PBYTE                           pOutBuffer,
    ULONG                           ulBytesAvailable,
    PULONG                          pulBytesUsed
    )
{
    *pulBytesUsed = 0;
    return STATUS_NOT_SUPPORTED;
}

NTSTATUS
SrvGetUnsupportedInfo(
    PSRV_EXEC_CONTEXT               pExecContext,
    PSRV_PROCESS_INFO_STATE         pInfoState,
    PGET_INFO_CLASS_DISPATCH_ENTRY  pInfoEntry
    )
{
    return STATUS_NOT_SUPPORTED;
}

NTSTATUS
SrvGetFileOrVolumeInfoGeneric(
    PSRV_EXEC_CONTEXT                   pExecContext,
    PSRV_PROCESS_INFO_STATE             pInfoState,
    PGET_INFO_CLASS_DISPATCH_ENTRY      pInfoEntry,
    PIO_QUERY_FILE_OR_VOLUME_INFO_FUNC  pQueryFunc
    )
{
    NTSTATUS                   ntStatus     = STATUS_SUCCESS;

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

    if (!pInfoState->pData)
    {
        if (pInfoState->ulOutputBufferLen <  pInfoEntry->ProtoStructSize)
        {
            ntStatus = STATUS_INFO_LENGTH_MISMATCH;
            BAIL_ON_NT_STATUS(ntStatus);
        }

        ntStatus = SrvAllocateMemory(
                        pInfoEntry->IOStructSize,
                        OUT_PPVOID(&pInfoState->pData));
        BAIL_ON_NT_STATUS(ntStatus);

        pInfoState->ulDataLen = pInfoEntry->IOStructSize;

        SrvPrepareExecContextAsync(pExecContext);

        ntStatus = pQueryFunc(
                        pInfoState->hFile,
                        pExecContext->pAcb,
                        &pExecContext->ioStatusBlock,
                        pInfoState->pData,
                        pInfoState->ulDataLen,
                        pInfoEntry->InfoClass);
        BAIL_ON_NT_STATUS(ntStatus);

        // synchronous completion
        SrvReleaseExecContextAsync(pExecContext);
    }

    pInfoState->ulActualDataLen =
        pExecContext->ioStatusBlock.BytesTransferred;

error:

    return ntStatus;
}

NTSTATUS
SrvGetFileOrVolumeInfoGeneric_WithString(
    PSRV_EXEC_CONTEXT                   pExecContext,
    PSRV_PROCESS_INFO_STATE             pInfoState,
    PGET_INFO_CLASS_DISPATCH_ENTRY      pInfoEntry,
    PIO_QUERY_FILE_OR_VOLUME_INFO_FUNC  pQueryFunc,
    BOOLEAN                             bHonorBufferLimit
    )
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    ULONG ulStructDelta = pInfoEntry->IOStructSize - pInfoEntry->ProtoStructSize;
    ULONG ulMaxSize = pInfoState->ulOutputBufferLen + ulStructDelta;

    if (!bHonorBufferLimit)
    {
        ulMaxSize = ~0;
    }

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

        if (!pInfoState->ulDataLen)
        {
            if (pInfoState->ulOutputBufferLen < pInfoEntry->ProtoStructSize)
            {
                ntStatus = STATUS_INFO_LENGTH_MISMATCH;
                BAIL_ON_NT_STATUS(ntStatus);
            }

            ulNewSize = pInfoEntry->IOStructSize + 256 * sizeof(wchar16_t);
        }
        else if (ntStatus == STATUS_SUCCESS)
        {
            break;
        }
        else if (pInfoState->ulDataLen >= ulMaxSize ||
                 (ntStatus != STATUS_BUFFER_OVERFLOW &&
                  ntStatus != STATUS_BUFFER_TOO_SMALL))
        {
            BAIL_ON_NT_STATUS(ntStatus);
        }
        else
        {
            ulNewSize = pInfoState->ulDataLen * 2;
        }

        ulNewSize = SMB_MIN(ulNewSize, ulMaxSize);

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

        pInfoState->ulDataLen = ulNewSize;

        SrvPrepareExecContextAsync(pExecContext);

        ntStatus = pQueryFunc(
                       pInfoState->hFile,
                       pExecContext->pAcb,
                       &pExecContext->ioStatusBlock,
                       pInfoState->pData,
                       pInfoState->ulDataLen,
                       pInfoEntry->InfoClass);
        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
SrvGetFileInfoGeneric_WithFileName(
    PSRV_EXEC_CONTEXT               pExecContext,
    PSRV_PROCESS_INFO_STATE         pInfoState,
    PGET_INFO_CLASS_DISPATCH_ENTRY  pInfoEntry
    )
{
    // File name information returned from the file system
    // includes local path information that must be stripped
    // when marshaling our response.  Since we don't know the
    // length of the prefix in advance, ignore the client
    // specified buffer limit.
    return SrvGetFileOrVolumeInfoGeneric_WithString(
                pExecContext,
                pInfoState,
                pInfoEntry,
                IoQueryInformationFile,
                /*bHonorBufferLimit*/FALSE);
}

NTSTATUS
SrvGetFileInfoGeneric(
    PSRV_EXEC_CONTEXT               pExecContext,
    PSRV_PROCESS_INFO_STATE         pInfoState,
    PGET_INFO_CLASS_DISPATCH_ENTRY  pInfoEntry
    )
{
    return SrvGetFileOrVolumeInfoGeneric(
                pExecContext,
                pInfoState,
                pInfoEntry,
                IoQueryInformationFile);
}

NTSTATUS
SrvGetFileInfoGeneric_WithString(
    PSRV_EXEC_CONTEXT               pExecContext,
    PSRV_PROCESS_INFO_STATE         pInfoState,
    PGET_INFO_CLASS_DISPATCH_ENTRY  pInfoEntry
    )
{
    return SrvGetFileOrVolumeInfoGeneric_WithString(
                pExecContext,
                pInfoState,
                pInfoEntry,
                IoQueryInformationFile,
                /*bHonorBufferLimit*/TRUE);
}

NTSTATUS
SrvGetFileSystemInfoGeneric(
    PSRV_EXEC_CONTEXT               pExecContext,
    PSRV_PROCESS_INFO_STATE         pInfoState,
    PGET_INFO_CLASS_DISPATCH_ENTRY  pInfoEntry
    )
{
    return SrvGetFileOrVolumeInfoGeneric(
                pExecContext,
                pInfoState,
                pInfoEntry,
                IoQueryVolumeInformationFile);
}

NTSTATUS
SrvGetFileSystemInfoGeneric_WithString(
    PSRV_EXEC_CONTEXT               pExecContext,
    PSRV_PROCESS_INFO_STATE         pInfoState,
    PGET_INFO_CLASS_DISPATCH_ENTRY  pInfoEntry
    )
{
    return SrvGetFileOrVolumeInfoGeneric_WithString(
                pExecContext,
                pInfoState,
                pInfoEntry,
                IoQueryVolumeInformationFile,
                /*bHonorBufferLimit*/TRUE);
}

NTSTATUS
SrvGetTableInfo(
    PSRV_EXEC_CONTEXT pExecContext,
    PSRV_PROCESS_INFO_STATE pInfoState,
    GET_INFO_CLASS_DISPATCH_ENTRY *Table,
    size_t TableNumElements
    )
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    USHORT usInfoClass = pInfoState->usInfoClass;
    PGET_INFO_CLASS_DISPATCH_ENTRY pInfoEntry = Table + usInfoClass;

    if (usInfoClass >= TableNumElements || !pInfoEntry->GetHandler)
    {
        ntStatus = STATUS_INVALID_INFO_CLASS;
        BAIL_ON_NT_STATUS(ntStatus);
    }

    if (pInfoState->ulOutputBufferLen < pInfoEntry->ProtoStructSize)
    {
        ntStatus = STATUS_INFO_LENGTH_MISMATCH;
        BAIL_ON_NT_STATUS(ntStatus);
    }

    ntStatus = pInfoEntry->GetHandler(pExecContext, pInfoState, pInfoEntry);
    BAIL_ON_NT_STATUS(ntStatus);

error:

    return ntStatus;
}

NTSTATUS
SrvMarshalTableInfo(
    PSRV_EXEC_CONTEXT pExecContext,
    PSRV_PROCESS_INFO_STATE pInfoState,
    GET_INFO_CLASS_DISPATCH_ENTRY *Table,
    size_t TableNumElements,
    PBYTE pDataCursor,
    ULONG ulBytesAvailable,
    PULONG pulBytesUsed
    )
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    USHORT usInfoClass = pInfoState->usInfoClass;
    PGET_INFO_CLASS_DISPATCH_ENTRY pInfoEntry = Table + usInfoClass;

    if (usInfoClass >= TableNumElements || !pInfoEntry->MarshalHandler)
    {
        ntStatus = STATUS_INVALID_INFO_CLASS;
        BAIL_ON_NT_STATUS(ntStatus);
    }

    if (pInfoState->ulOutputBufferLen < pInfoEntry->ProtoStructSize)
    {
        ntStatus = STATUS_INFO_LENGTH_MISMATCH;
        BAIL_ON_NT_STATUS(ntStatus);
    }

    ntStatus = pInfoEntry->MarshalHandler(
                    pExecContext,
                    pInfoState,
                    pInfoEntry,
                    pDataCursor,
                    ulBytesAvailable,
                    pulBytesUsed);
    BAIL_ON_NT_STATUS(ntStatus);

error:

    return ntStatus;
}
