/* -*- 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 (c) 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@likewise.com
 */

#include "includes.h"

NTSTATUS
SrvShadowCopyCreateQueryBuffer(
    IN ULONG OutputBufferSize,
    IN OUT PUSHORT pSnapshotListSize,
    OUT PVOID* ppSnapshotList
    )
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    ULONG snapshotArrayHeaderFooterSize = 0;
    ULONG gmtTokenSize = 0;
    ULONG snapshotListSize = 0;
    PVOID pSnapshotList = NULL;

    if (OutputBufferSize < sizeof(SMB_SRV_SNAPSHOT_ARRAY_HEADER))
    {
        ntStatus = STATUS_BUFFER_TOO_SMALL;
        BAIL_ON_NT_STATUS(ntStatus);
    }

    // The snapshot array consists of a SMB_SRV_SNAPSHOT_ARRAY_HEADER
    // followed by a series of @GMT tokens and terminated with NULL WCHAR.
    snapshotArrayHeaderFooterSize = sizeof(SMB_SRV_SNAPSHOT_ARRAY_HEADER) +
                                    sizeof(WCHAR);

    // Each @GMT token has a fixed size of 50 bytes.
    gmtTokenSize = sizeof(GMT_TOKEN_TEMPLATE) * sizeof(WCHAR);

    // The interim buffer is the buffer that is passed to the filesystem.
    // Its size is determined by the number of @GMT tokens that we'll be
    // able to fit in the the response buffer based on the outputBufferSize
    // received from the client.
    snapshotListSize = LW_FIELD_OFFSET(IO_SNAPSHOT_LIST, SnapshotList) +
                        ((OutputBufferSize - snapshotArrayHeaderFooterSize) /
                            gmtTokenSize) *
                        sizeof(LONG64);
    ntStatus = SrvAllocateMemory(snapshotListSize, &pSnapshotList);
    BAIL_ON_NT_STATUS(ntStatus);

error:
    if (ntStatus)
    {
        SRV_SAFE_FREE_MEMORY_AND_RESET(pSnapshotList);
        snapshotListSize = 0;
    }

    *pSnapshotListSize = snapshotListSize;
    *ppSnapshotList = pSnapshotList;

    return ntStatus;
}

NTSTATUS
SrvShadowCopyCreateEnumerateResponse(
    IN ULONG AllowedOutputBufferSize,
    IN PIO_SNAPSHOT_LIST pSnapshotList,
    OUT PULONG pActualOutputBufferSize,
    OUT PVOID* ppSnapshotArray
    )
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    PVOID pSnapshotArray = NULL;
    PSMB_SRV_SNAPSHOT_ARRAY_HEADER pSnapshotArrayHeader = NULL;
    ULONG gmtTokenSize = 0;
    ULONG snapshotArraySize = 0;
    ULONG arrayHeaderSize = 0;
    ULONG actualOutputBufferSize = 0;
    ULONG numberOfSnapshotsReturned = 0;
    ULONG i = 0;
    PWCHAR pToken = NULL;

    // Each @GMT token has a fixed size of 50 bytes.
    gmtTokenSize = sizeof(GMT_TOKEN_TEMPLATE) * sizeof(WCHAR);

    // How much space do we need to accommodate all the snapshots plus a NULL?
    snapshotArraySize = (gmtTokenSize * pSnapshotList->NumberOfSnapshots) +
                        sizeof(WCHAR);

    arrayHeaderSize = sizeof(*pSnapshotArrayHeader);
    if (arrayHeaderSize + snapshotArraySize <= AllowedOutputBufferSize)
    {
        // The client gave us enough room to return all the snapshots.  Fill it.
        actualOutputBufferSize = arrayHeaderSize + snapshotArraySize;
        ntStatus = SrvAllocateMemory(actualOutputBufferSize, &pSnapshotArray);
        BAIL_ON_NT_STATUS(ntStatus);

        pToken = (PWCHAR)LwRtlOffsetToPointer(pSnapshotArray, arrayHeaderSize);

        // Convert each snapshot to an @GMT token and insert in buffer
        for (i = 0; i < pSnapshotList->NumberOfSnapshotsReturned; i++)
        {
            ntStatus = NTTimeToGmtToken(
                            pSnapshotList->SnapshotList[i],
                            pToken);
            BAIL_ON_NT_STATUS(ntStatus);

            pToken += sizeof(GMT_TOKEN_TEMPLATE);
        }

        numberOfSnapshotsReturned = pSnapshotList->NumberOfSnapshotsReturned;
    }
    else
    {
        // If we don't have enough room to return all the snapshots
        actualOutputBufferSize = arrayHeaderSize;
        ntStatus = SrvAllocateMemory(actualOutputBufferSize, &pSnapshotArray);
        BAIL_ON_NT_STATUS(ntStatus);

        numberOfSnapshotsReturned = 0;
    }

    pSnapshotArrayHeader = (PSMB_SRV_SNAPSHOT_ARRAY_HEADER)pSnapshotArray;
    pSnapshotArrayHeader->NumberOfSnapshots = pSnapshotList->NumberOfSnapshots;
    pSnapshotArrayHeader->NumberOfSnapshotsReturned = numberOfSnapshotsReturned;

    // SnapshotArraySize is the number of available snapshots.  Returning this
    // lets the client know how big a buffer to allow next time.
    pSnapshotArrayHeader->SnapshotArraySize = snapshotArraySize;

error:
    if (ntStatus)
    {
        SRV_SAFE_FREE_MEMORY_AND_RESET(pSnapshotArray);
        actualOutputBufferSize = 0;
    }

    *pActualOutputBufferSize = actualOutputBufferSize;
    *ppSnapshotArray = pSnapshotArray;

    return ntStatus;
}
