/* $Id: VBoxGuestR0LibSharedFolders.c $ */
/** @file
 * VBoxGuestR0LibSharedFolders - Ring 0 Shared Folders calls.
 */

/*
 * Copyright (C) 2006-2020 Oracle Corporation
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */


/*********************************************************************************************************************************
*   Header Files                                                                                                                 *
*********************************************************************************************************************************/
#define LOG_GROUP LOG_GROUP_SHARED_FOLDERS
#include "VBoxGuestR0LibInternal.h"
#include <VBox/VBoxGuestLibSharedFolders.h>
#include <VBox/log.h>
#include <iprt/err.h>
#include <iprt/time.h>
#include <iprt/mem.h>
#include <iprt/path.h>
#include <iprt/string.h>

#ifdef VBGL_VBOXGUEST
# error "This file shouldn't be part of the VBoxGuestR0LibBase library that is linked into VBoxGuest.  It's client code."
#endif


/*********************************************************************************************************************************
*   Defined Constants And Macros                                                                                                 *
*********************************************************************************************************************************/
#define VBOX_INIT_CALL(a, b, c) \
    LogFunc(("%s, idClient=%d\n", "SHFL_FN_" # b, (c)->idClient)); \
    VBGL_HGCM_HDR_INIT(a, (c)->idClient, SHFL_FN_##b, SHFL_CPARMS_##b); \
    (a)->fInterruptible = false /* Currently we do like nfs with -o hard (default). */

#define VBOX_INIT_CALL_EX(a, b, c, a_cbReq) \
    LogFunc(("%s, idClient=%d\n", "SHFL_FN_" # b, (c)->idClient)); \
    VBGL_HGCM_HDR_INIT_EX(a, (c)->idClient, SHFL_FN_##b, SHFL_CPARMS_##b, a_cbReq); \
    (a)->fInterruptible = false /* Currently we do like nfs with -o hard (default). */



DECLVBGL(int) VbglR0SfInit(void)
{
    return VbglR0InitClient();
}

DECLVBGL(void) VbglR0SfTerm(void)
{
    VbglR0TerminateClient();
}

DECLVBGL(int) VbglR0SfConnect(PVBGLSFCLIENT pClient)
{
    int rc = VbglR0HGCMConnect(&pClient->handle, "VBoxSharedFolders", &pClient->idClient);
    if (RT_SUCCESS(rc))
        LogFunc(("idClient=%d\n", pClient->idClient));
    else
        LogFunc(("VbglR0HGCMConnect failed -> rc=%Rrc\n", rc));
    return rc;
}

DECLVBGL(void) VbglR0SfDisconnect(PVBGLSFCLIENT pClient)
{
    int rc;
    LogFunc(("u32ClientID=%d\n", pClient->idClient));
    if (pClient->handle == NULL)
        return;                 /* not connected */

    rc = VbglR0HGCMDisconnect(pClient->handle, pClient->idClient);
    NOREF(rc);
/*    Log(("VBOXSF: VbglR0SfDisconnect: VbglR0HGCMDisconnect -> %#x\n", rc)); */
    pClient->idClient = 0;
    pClient->handle   = NULL;
    return;
}

#if !defined(RT_OS_LINUX)

# ifndef RT_OS_WINDOWS

DECLVBGL(int) VbglR0SfSetUtf8(PVBGLSFCLIENT pClient)
{
    int rc;
    VBGLIOCHGCMCALL callInfo;

    VBOX_INIT_CALL(&callInfo, SET_UTF8, pClient);
    rc = VbglR0HGCMCall(pClient->handle, &callInfo, sizeof(callInfo));
/*    Log(("VBOXSF: VbglR0SfSetUtf8: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */
    return rc;
}

# endif /* !RT_OS_WINDOWS */

/** @name       Deprecated VBGL shared folder helpers.
 *
 * @deprecated  These are all use the slow VbglR0HGCMCall interface, that
 *              basically treat ring-0 and user land callers much the same.
 *              Since 6.0 there is VbglR0HGCMFastCall() that does not bother with
 *              repacking the request and locking/duplicating parameter buffers,
 *              but just passes it along to the host and handles the waiting.
 *              Also new in 6.0 is embedded buffers which saves a bit time on
 *              guest and host by embedding parameter buffers into the request.
 *
 * @{
 */

DECLVBGL(int) VbglR0SfQueryMappings(PVBGLSFCLIENT pClient, SHFLMAPPING paMappings[], uint32_t *pcMappings)
{
    int rc;
    VBoxSFQueryMappings data;

    VBOX_INIT_CALL(&data.callInfo, QUERY_MAPPINGS, pClient);

    data.flags.type                      = VMMDevHGCMParmType_32bit;
    data.flags.u.value32                 = SHFL_MF_UCS2;

    data.numberOfMappings.type           = VMMDevHGCMParmType_32bit;
    data.numberOfMappings.u.value32      = *pcMappings;

    data.mappings.type                   = VMMDevHGCMParmType_LinAddr;
    data.mappings.u.Pointer.size         = sizeof(SHFLMAPPING) * *pcMappings;
    data.mappings.u.Pointer.u.linearAddr = (uintptr_t)&paMappings[0];

/*    Log(("VBOXSF: in ifs difference %d\n", (char *)&data.flags.type - (char *)&data.callInfo.cParms)); */
    rc = VbglR0HGCMCall(pClient->handle, &data.callInfo, sizeof(data));
/*    Log(("VBOXSF: VbglR0SfQueryMappings: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.result)); */
    if (RT_SUCCESS(rc))
        *pcMappings = data.numberOfMappings.u.value32;

    return rc;
}

DECLVBGL(int) VbglR0SfQueryMapName(PVBGLSFCLIENT pClient, SHFLROOT root, SHFLSTRING *pString, uint32_t size)
{
    int rc;
    VBoxSFQueryMapName data;

    VBOX_INIT_CALL(&data.callInfo, QUERY_MAP_NAME, pClient);

    data.root.type                   = VMMDevHGCMParmType_32bit;
    data.root.u.value32              = root;

    data.name.type                   = VMMDevHGCMParmType_LinAddr;
    data.name.u.Pointer.size         = size;
    data.name.u.Pointer.u.linearAddr = (uintptr_t)pString;

    rc = VbglR0HGCMCall(pClient->handle, &data.callInfo, sizeof(data));
/*    Log(("VBOXSF: VbglR0SfQueryMapName: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */
    return rc;
}

DECLVBGL(int) VbglR0SfMapFolder(PVBGLSFCLIENT pClient, PSHFLSTRING szFolderName, PVBGLSFMAP pMap)
{
    int rc;
    VBoxSFMapFolder data;

    VBOX_INIT_CALL(&data.callInfo, MAP_FOLDER, pClient);

    data.path.type                    = VMMDevHGCMParmType_LinAddr;
    data.path.u.Pointer.size          = ShflStringSizeOfBuffer(szFolderName);
    data.path.u.Pointer.u.linearAddr  = (uintptr_t)szFolderName;

    data.root.type                    = VMMDevHGCMParmType_32bit;
    data.root.u.value32               = 0;

    data.delimiter.type               = VMMDevHGCMParmType_32bit;
    data.delimiter.u.value32          = RTPATH_DELIMITER;

    data.fCaseSensitive.type          = VMMDevHGCMParmType_32bit;
#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
    data.fCaseSensitive.u.value32     = 0;
#else
    data.fCaseSensitive.u.value32     = 1;
#endif

    rc = VbglR0HGCMCallRaw(pClient->handle, &data.callInfo, sizeof(data));
/*    Log(("VBOXSF: VbglR0SfMapFolder: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */
    if (RT_SUCCESS(rc))
    {
        pMap->root = data.root.u.value32;
        rc         = data.callInfo.Hdr.rc;
    }
    return rc;
}

DECLVBGL(int) VbglR0SfUnmapFolder(PVBGLSFCLIENT pClient, PVBGLSFMAP pMap)
{
    int rc;
    VBoxSFUnmapFolder data;

    VBOX_INIT_CALL(&data.callInfo, UNMAP_FOLDER, pClient);

    data.root.type                      = VMMDevHGCMParmType_32bit;
    data.root.u.value32                 = pMap->root;

    rc = VbglR0HGCMCall(pClient->handle, &data.callInfo, sizeof(data));
/*    Log(("VBOXSF: VbglR0SfUnmapFolder: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */
    return rc;
}

# if !defined(RT_OS_WINDOWS)

DECLVBGL(int) VbglR0SfCreate(PVBGLSFCLIENT pClient, PVBGLSFMAP pMap, PSHFLSTRING pParsedPath, PSHFLCREATEPARMS pCreateParms)
{
    /** @todo copy buffers to physical or mapped memory. */
    int rc;
    VBoxSFCreate data;

    VBOX_INIT_CALL(&data.callInfo, CREATE, pClient);

    data.root.type                    = VMMDevHGCMParmType_32bit;
    data.root.u.value32               = pMap->root;

    data.path.type                    = VMMDevHGCMParmType_LinAddr;
    data.path.u.Pointer.size          = ShflStringSizeOfBuffer (pParsedPath);
    data.path.u.Pointer.u.linearAddr  = (uintptr_t)pParsedPath;

    data.parms.type                   = VMMDevHGCMParmType_LinAddr;
    data.parms.u.Pointer.size         = sizeof(SHFLCREATEPARMS);
    data.parms.u.Pointer.u.linearAddr = (uintptr_t)pCreateParms;

    rc = VbglR0HGCMCall(pClient->handle, &data.callInfo, sizeof(data));
/*    Log(("VBOXSF: VbglR0SfCreate: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */
    return rc;
}

DECLVBGL(int) VbglR0SfClose(PVBGLSFCLIENT pClient, PVBGLSFMAP pMap, SHFLHANDLE Handle)
{
    int rc;
    VBoxSFClose data;

    VBOX_INIT_CALL(&data.callInfo, CLOSE, pClient);

    data.root.type                      = VMMDevHGCMParmType_32bit;
    data.root.u.value32                 = pMap->root;

    data.handle.type                    = VMMDevHGCMParmType_64bit;
    data.handle.u.value64               = Handle;

    rc = VbglR0HGCMCall(pClient->handle, &data.callInfo, sizeof(data));
/*    Log(("VBOXSF: VbglR0SfClose: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */
    return rc;
}


DECLVBGL(int) VbglR0SfRemove(PVBGLSFCLIENT pClient, PVBGLSFMAP pMap, PSHFLSTRING pParsedPath, uint32_t flags)
{
    int rc = VINF_SUCCESS;

    VBoxSFRemove data;

    VBOX_INIT_CALL(&data.callInfo, REMOVE, pClient);

    data.root.type                      = VMMDevHGCMParmType_32bit;
    data.root.u.value32                 = pMap->root;

    data.path.type                      = VMMDevHGCMParmType_LinAddr_In;
    data.path.u.Pointer.size            = ShflStringSizeOfBuffer(pParsedPath);
    data.path.u.Pointer.u.linearAddr    = (uintptr_t)pParsedPath;

    data.flags.type                     = VMMDevHGCMParmType_32bit;
    data.flags.u.value32                = flags;

    rc = VbglR0HGCMCall(pClient->handle, &data.callInfo, sizeof(data));
/*    Log(("VBOXSF: VbglR0SfRemove: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */
    return rc;
}

DECLVBGL(int) VbglR0SfRename(PVBGLSFCLIENT pClient, PVBGLSFMAP pMap, PSHFLSTRING pSrcPath, PSHFLSTRING pDestPath, uint32_t flags)
{
    int rc;
    VBoxSFRename data;

    VBOX_INIT_CALL(&data.callInfo, RENAME, pClient);

    data.root.type                      = VMMDevHGCMParmType_32bit;
    data.root.u.value32                 = pMap->root;

    data.src.type                       = VMMDevHGCMParmType_LinAddr_In;
    data.src.u.Pointer.size             = ShflStringSizeOfBuffer(pSrcPath);
    data.src.u.Pointer.u.linearAddr     = (uintptr_t)pSrcPath;

    data.dest.type                      = VMMDevHGCMParmType_LinAddr_In;
    data.dest.u.Pointer.size            = ShflStringSizeOfBuffer(pDestPath);
    data.dest.u.Pointer.u.linearAddr    = (uintptr_t)pDestPath;

    data.flags.type                     = VMMDevHGCMParmType_32bit;
    data.flags.u.value32                = flags;

    rc = VbglR0HGCMCall(pClient->handle, &data.callInfo, sizeof(data));
/*    Log(("VBOXSF: VbglR0SfRename: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */
    return rc;
}

DECLVBGL(int) VbglR0SfRead(PVBGLSFCLIENT pClient, PVBGLSFMAP pMap, SHFLHANDLE hFile,
                           uint64_t offset, uint32_t *pcbBuffer, uint8_t *pBuffer, bool fLocked)
{
    int rc;
    VBoxSFRead data;

    VBOX_INIT_CALL(&data.callInfo, READ, pClient);

    data.root.type                      = VMMDevHGCMParmType_32bit;
    data.root.u.value32                 = pMap->root;

    data.handle.type                    = VMMDevHGCMParmType_64bit;
    data.handle.u.value64               = hFile;
    data.offset.type                    = VMMDevHGCMParmType_64bit;
    data.offset.u.value64               = offset;
    data.cb.type                        = VMMDevHGCMParmType_32bit;
    data.cb.u.value32                   = *pcbBuffer;
    data.buffer.type                    = (fLocked) ? VMMDevHGCMParmType_LinAddr_Locked_Out : VMMDevHGCMParmType_LinAddr_Out;
    data.buffer.u.Pointer.size          = *pcbBuffer;
    data.buffer.u.Pointer.u.linearAddr  = (uintptr_t)pBuffer;

    rc = VbglR0HGCMCallRaw(pClient->handle, &data.callInfo, sizeof(data));
/*    Log(("VBOXSF: VbglR0SfRead: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */
    if (RT_SUCCESS(rc))
    {
        rc = data.callInfo.Hdr.rc;
        *pcbBuffer = data.cb.u.value32;
    }
    return rc;
}

DECLVBGL(int) VbglR0SfReadPageList(PVBGLSFCLIENT pClient, PVBGLSFMAP pMap, SHFLHANDLE hFile, uint64_t offset, uint32_t *pcbBuffer,
                                   uint16_t offFirstPage, uint16_t cPages, RTGCPHYS64 *paPages)
{
    uint32_t            cbToRead  = *pcbBuffer;
    uint32_t            cbData    = (uint32_t)(sizeof(VBoxSFRead) + RT_UOFFSETOF_DYN(HGCMPageListInfo, aPages[cPages]));
    VBoxSFRead         *pData     = (VBoxSFRead *)RTMemTmpAlloc(cbData);
    HGCMPageListInfo   *pPgLst    = (HGCMPageListInfo *)(pData + 1);
    uint16_t            iPage;
    int                 rc;

    if (RT_UNLIKELY(!pData))
        return VERR_NO_TMP_MEMORY;

    VBOX_INIT_CALL_EX(&pData->callInfo, READ, pClient, cbData);

    pData->root.type                      = VMMDevHGCMParmType_32bit;
    pData->root.u.value32                 = pMap->root;

    pData->handle.type                    = VMMDevHGCMParmType_64bit;
    pData->handle.u.value64               = hFile;
    pData->offset.type                    = VMMDevHGCMParmType_64bit;
    pData->offset.u.value64               = offset;
    pData->cb.type                        = VMMDevHGCMParmType_32bit;
    pData->cb.u.value32                   = cbToRead;
    pData->buffer.type                    = VMMDevHGCMParmType_PageList;
    pData->buffer.u.PageList.size         = cbToRead;
    pData->buffer.u.PageList.offset       = sizeof(VBoxSFRead);

    pPgLst->flags = VBOX_HGCM_F_PARM_DIRECTION_FROM_HOST;
    pPgLst->offFirstPage = offFirstPage;
    pPgLst->cPages = cPages;
    for (iPage = 0; iPage < cPages; iPage++)
        pPgLst->aPages[iPage] = paPages[iPage];

    rc = VbglR0HGCMCallRaw(pClient->handle, &pData->callInfo, cbData);
/*    Log(("VBOXSF: VbglR0SfReadPageList: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */
    if (RT_SUCCESS(rc))
    {
        rc = pData->callInfo.Hdr.rc;
        *pcbBuffer = pData->cb.u.value32;
    }

    RTMemTmpFree(pData);
    return rc;
}

DECLVBGL(int) VbglR0SfWrite(PVBGLSFCLIENT pClient, PVBGLSFMAP pMap, SHFLHANDLE hFile,
                            uint64_t offset, uint32_t *pcbBuffer, uint8_t *pBuffer, bool fLocked)
{
    int rc;
    VBoxSFWrite data;

    VBOX_INIT_CALL(&data.callInfo, WRITE, pClient);

    data.root.type                      = VMMDevHGCMParmType_32bit;
    data.root.u.value32                 = pMap->root;

    data.handle.type                    = VMMDevHGCMParmType_64bit;
    data.handle.u.value64               = hFile;
    data.offset.type                    = VMMDevHGCMParmType_64bit;
    data.offset.u.value64               = offset;
    data.cb.type                        = VMMDevHGCMParmType_32bit;
    data.cb.u.value32                   = *pcbBuffer;
    data.buffer.type                    = fLocked ? VMMDevHGCMParmType_LinAddr_Locked_In : VMMDevHGCMParmType_LinAddr_In;
    data.buffer.u.Pointer.size          = *pcbBuffer;
    data.buffer.u.Pointer.u.linearAddr  = (uintptr_t)pBuffer;

    rc = VbglR0HGCMCallRaw(pClient->handle, &data.callInfo, sizeof(data));
/*    Log(("VBOXSF: VbglR0SfWrite: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */
    if (RT_SUCCESS(rc))
    {
        rc = data.callInfo.Hdr.rc;
        *pcbBuffer = data.cb.u.value32;
    }
    return rc;
}

DECLVBGL(int) VbglR0SfWritePhysCont(PVBGLSFCLIENT pClient, PVBGLSFMAP pMap, SHFLHANDLE hFile, uint64_t offset,
                                    uint32_t *pcbBuffer, RTCCPHYS PhysBuffer)
{
    uint32_t            cbToWrite = *pcbBuffer;
    uint32_t            cPages    = RT_ALIGN_32((PhysBuffer & PAGE_OFFSET_MASK) + cbToWrite, PAGE_SIZE) >> PAGE_SHIFT;
    uint32_t            cbData    = (uint32_t)(sizeof(VBoxSFWrite) + RT_UOFFSETOF_DYN(HGCMPageListInfo, aPages[cPages]));
    VBoxSFWrite        *pData     = (VBoxSFWrite *)RTMemTmpAlloc(cbData);
    HGCMPageListInfo   *pPgLst    = (HGCMPageListInfo *)(pData + 1);
    uint32_t            iPage;
    int                 rc;

    if (RT_UNLIKELY(!pData))
        return VERR_NO_TMP_MEMORY;

    VBOX_INIT_CALL_EX(&pData->callInfo, WRITE, pClient, cbData);

    pData->root.type                      = VMMDevHGCMParmType_32bit;
    pData->root.u.value32                 = pMap->root;

    pData->handle.type                    = VMMDevHGCMParmType_64bit;
    pData->handle.u.value64               = hFile;
    pData->offset.type                    = VMMDevHGCMParmType_64bit;
    pData->offset.u.value64               = offset;
    pData->cb.type                        = VMMDevHGCMParmType_32bit;
    pData->cb.u.value32                   = cbToWrite;
    pData->buffer.type                    = VMMDevHGCMParmType_PageList;
    pData->buffer.u.PageList.size         = cbToWrite;
    pData->buffer.u.PageList.offset       = sizeof(VBoxSFWrite);

    pPgLst->flags = VBOX_HGCM_F_PARM_DIRECTION_TO_HOST;
    pPgLst->offFirstPage = (uint16_t)(PhysBuffer & PAGE_OFFSET_MASK);
    pPgLst->cPages = cPages;
    PhysBuffer &= ~(RTCCPHYS)PAGE_OFFSET_MASK;
    for (iPage = 0; iPage < cPages; iPage++, PhysBuffer += PAGE_SIZE)
        pPgLst->aPages[iPage] = PhysBuffer;

    rc = VbglR0HGCMCallRaw(pClient->handle, &pData->callInfo, cbData);
/*    Log(("VBOXSF: VbglR0SfWritePhysCont: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */
    if (RT_SUCCESS(rc))
    {
        rc = pData->callInfo.Hdr.rc;
        *pcbBuffer = pData->cb.u.value32;
    }

    RTMemTmpFree(pData);
    return rc;

}

DECLVBGL(int) VbglR0SfWritePageList(PVBGLSFCLIENT pClient, PVBGLSFMAP pMap, SHFLHANDLE hFile, uint64_t offset, uint32_t *pcbBuffer,
                                    uint16_t offFirstPage, uint16_t cPages, RTGCPHYS64 *paPages)
{
    uint32_t            cbToWrite = *pcbBuffer;
    uint32_t            cbData    = (uint32_t)(sizeof(VBoxSFWrite) + RT_UOFFSETOF_DYN(HGCMPageListInfo, aPages[cPages]));
    VBoxSFWrite        *pData     = (VBoxSFWrite *)RTMemTmpAlloc(cbData);
    HGCMPageListInfo   *pPgLst    = (HGCMPageListInfo *)(pData + 1);
    uint16_t            iPage;
    int                 rc;

    if (RT_UNLIKELY(!pData))
        return VERR_NO_TMP_MEMORY;

    VBOX_INIT_CALL_EX(&pData->callInfo, WRITE, pClient, cbData);

    pData->root.type                      = VMMDevHGCMParmType_32bit;
    pData->root.u.value32                 = pMap->root;

    pData->handle.type                    = VMMDevHGCMParmType_64bit;
    pData->handle.u.value64               = hFile;
    pData->offset.type                    = VMMDevHGCMParmType_64bit;
    pData->offset.u.value64               = offset;
    pData->cb.type                        = VMMDevHGCMParmType_32bit;
    pData->cb.u.value32                   = cbToWrite;
    pData->buffer.type                    = VMMDevHGCMParmType_PageList;
    pData->buffer.u.PageList.size         = cbToWrite;
    pData->buffer.u.PageList.offset       = sizeof(VBoxSFWrite);

    pPgLst->flags = VBOX_HGCM_F_PARM_DIRECTION_TO_HOST;
    pPgLst->offFirstPage = offFirstPage;
    pPgLst->cPages = cPages;
    for (iPage = 0; iPage < cPages; iPage++)
        pPgLst->aPages[iPage] = paPages[iPage];

    rc = VbglR0HGCMCallRaw(pClient->handle, &pData->callInfo, cbData);
/*    Log(("VBOXSF: VbglR0SfWritePageList: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */
    if (RT_SUCCESS(rc))
    {
        rc = pData->callInfo.Hdr.rc;
        *pcbBuffer = pData->cb.u.value32;
    }

    RTMemTmpFree(pData);
    return rc;
}

# endif /* !RT_OS_WINDOWS */

DECLVBGL(int) VbglR0SfFlush(PVBGLSFCLIENT pClient, PVBGLSFMAP pMap, SHFLHANDLE hFile)
{
    int rc;
    VBoxSFFlush data;

    VBOX_INIT_CALL(&data.callInfo, FLUSH, pClient);

    data.root.type                      = VMMDevHGCMParmType_32bit;
    data.root.u.value32                 = pMap->root;

    data.handle.type                    = VMMDevHGCMParmType_64bit;
    data.handle.u.value64               = hFile;

    rc = VbglR0HGCMCall(pClient->handle, &data.callInfo, sizeof(data));
/*    Log(("VBOXSF: VbglR0SfFlush: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */
    return rc;
}

DECLVBGL(int) VbglR0SfDirInfo(
    PVBGLSFCLIENT pClient,
    PVBGLSFMAP pMap,
    SHFLHANDLE hFile,
    PSHFLSTRING ParsedPath,
    uint32_t flags,
    uint32_t index,
    uint32_t *pcbBuffer,
    PSHFLDIRINFO pBuffer,
    uint32_t *pcFiles)
{
    int rc;
    VBoxSFList data;

    VBOX_INIT_CALL(&data.callInfo, LIST, pClient);

    data.root.type                      = VMMDevHGCMParmType_32bit;
    data.root.u.value32                 = pMap->root;

    data.handle.type                    = VMMDevHGCMParmType_64bit;
    data.handle.u.value64               = hFile;
    data.flags.type                     = VMMDevHGCMParmType_32bit;
    data.flags.u.value32                = flags;
    data.cb.type                        = VMMDevHGCMParmType_32bit;
    data.cb.u.value32                   = *pcbBuffer;
    data.path.type                      = VMMDevHGCMParmType_LinAddr_In;
    data.path.u.Pointer.size            = ParsedPath ? ShflStringSizeOfBuffer(ParsedPath) : 0;
    data.path.u.Pointer.u.linearAddr    = (uintptr_t) ParsedPath;

    data.buffer.type                    = VMMDevHGCMParmType_LinAddr_Out;
    data.buffer.u.Pointer.size          = *pcbBuffer;
    data.buffer.u.Pointer.u.linearAddr  = (uintptr_t)pBuffer;

    data.resumePoint.type               = VMMDevHGCMParmType_32bit;
    data.resumePoint.u.value32          = index;
    data.cFiles.type                    = VMMDevHGCMParmType_32bit;
    data.cFiles.u.value32               = 0; /* out parameters only */

    rc = VbglR0HGCMCall(pClient->handle, &data.callInfo, sizeof(data));
/*    Log(("VBOXSF: VbglR0SfDirInfo: rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */
    *pcbBuffer = data.cb.u.value32;
    *pcFiles   = data.cFiles.u.value32;
    return rc;
}

# ifndef RT_OS_WINDOWS

DECLVBGL(int) VbglR0SfFsInfo(PVBGLSFCLIENT pClient, PVBGLSFMAP pMap, SHFLHANDLE hFile,
                             uint32_t flags, uint32_t *pcbBuffer, PSHFLDIRINFO pBuffer)
{
    int rc;
    VBoxSFInformation data;

    VBOX_INIT_CALL(&data.callInfo, INFORMATION, pClient);

    data.root.type                      = VMMDevHGCMParmType_32bit;
    data.root.u.value32                 = pMap->root;

    data.handle.type                    = VMMDevHGCMParmType_64bit;
    data.handle.u.value64               = hFile;
    data.flags.type                     = VMMDevHGCMParmType_32bit;
    data.flags.u.value32                = flags;
    data.cb.type                        = VMMDevHGCMParmType_32bit;
    data.cb.u.value32                   = *pcbBuffer;
    data.info.type                      = VMMDevHGCMParmType_LinAddr;
    data.info.u.Pointer.size            = *pcbBuffer;
    data.info.u.Pointer.u.linearAddr    = (uintptr_t)pBuffer;

    rc = VbglR0HGCMCallRaw(pClient->handle, &data.callInfo, sizeof(data));
/*    Log(("VBOXSF: VbglR0SfFsInfo: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */
    if (RT_SUCCESS(rc))
    {
        rc = data.callInfo.Hdr.rc;
        *pcbBuffer = data.cb.u.value32;
    }
    return rc;
}

# endif /* !RT_OS_WINDOWS */

DECLVBGL(int) VbglR0SfLock(PVBGLSFCLIENT pClient, PVBGLSFMAP pMap, SHFLHANDLE hFile,
                           uint64_t offset, uint64_t cbSize, uint32_t fLock)
{
    int rc;
    VBoxSFLock data;

    VBOX_INIT_CALL(&data.callInfo, LOCK, pClient);

    data.root.type                      = VMMDevHGCMParmType_32bit;
    data.root.u.value32                 = pMap->root;

    data.handle.type                    = VMMDevHGCMParmType_64bit;
    data.handle.u.value64               = hFile;
    data.offset.type                    = VMMDevHGCMParmType_64bit;
    data.offset.u.value64               = offset;
    data.length.type                    = VMMDevHGCMParmType_64bit;
    data.length.u.value64               = cbSize;

    data.flags.type                     = VMMDevHGCMParmType_32bit;
    data.flags.u.value32                = fLock;

    rc = VbglR0HGCMCall(pClient->handle, &data.callInfo, sizeof(data));
/*    Log(("VBOXSF: VbglR0SfLock: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */
    return rc;
}

# ifndef RT_OS_WINDOWS

DECLVBGL(int) VbglR0SfReadLink(PVBGLSFCLIENT pClient, PVBGLSFMAP pMap, PSHFLSTRING pParsedPath, uint32_t cbBuffer, uint8_t *pBuffer)
{
    int rc;
    VBoxSFReadLink data;

    VBOX_INIT_CALL(&data.callInfo, READLINK, pClient);

    data.root.type                      = VMMDevHGCMParmType_32bit;
    data.root.u.value32                 = pMap->root;

    data.path.type                      = VMMDevHGCMParmType_LinAddr_In;
    data.path.u.Pointer.size            = ShflStringSizeOfBuffer (pParsedPath);
    data.path.u.Pointer.u.linearAddr    = (uintptr_t)pParsedPath;

    data.buffer.type                    = VMMDevHGCMParmType_LinAddr_Out;
    data.buffer.u.Pointer.size          = cbBuffer;
    data.buffer.u.Pointer.u.linearAddr  = (uintptr_t)pBuffer;

    rc = VbglR0HGCMCall(pClient->handle, &data.callInfo, sizeof(data));
/*    Log(("VBOXSF: VbglR0SfReadLink: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */
    return rc;
}

DECLVBGL(int) VbglR0SfSymlink(PVBGLSFCLIENT pClient, PVBGLSFMAP pMap, PSHFLSTRING pNewPath, PSHFLSTRING pOldPath,
                              PSHFLFSOBJINFO pBuffer)
{
    int rc;
    VBoxSFSymlink data;

    VBOX_INIT_CALL(&data.callInfo, SYMLINK, pClient);

    data.root.type                      = VMMDevHGCMParmType_32bit;
    data.root.u.value32                 = pMap->root;

    data.newPath.type                   = VMMDevHGCMParmType_LinAddr_In;
    data.newPath.u.Pointer.size         = ShflStringSizeOfBuffer (pNewPath);
    data.newPath.u.Pointer.u.linearAddr = (uintptr_t)pNewPath;

    data.oldPath.type                   = VMMDevHGCMParmType_LinAddr_In;
    data.oldPath.u.Pointer.size         = ShflStringSizeOfBuffer (pOldPath);
    data.oldPath.u.Pointer.u.linearAddr = (uintptr_t)pOldPath;

    data.info.type                      = VMMDevHGCMParmType_LinAddr_Out;
    data.info.u.Pointer.size            = sizeof(SHFLFSOBJINFO);
    data.info.u.Pointer.u.linearAddr    = (uintptr_t)pBuffer;

    rc = VbglR0HGCMCall(pClient->handle, &data.callInfo, sizeof(data));
/*    Log(("VBOXSF: VbglR0SfSymlink: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */
    return rc;
}

DECLVBGL(int) VbglR0SfSetSymlinks(PVBGLSFCLIENT pClient)
{
    int rc;
    VBGLIOCHGCMCALL callInfo;

    VBOX_INIT_CALL(&callInfo, SET_SYMLINKS, pClient);
    rc = VbglR0HGCMCall(pClient->handle, &callInfo, sizeof(callInfo));
/*    Log(("VBOXSF: VbglR0SfSetSymlinks: VbglR0HGCMCall rc = %#x, result = %#x\n", rc, data.callInfo.Hdr.rc)); */
    return rc;
}

# endif /* !RT_OS_WINDOWS */

#endif /* !RT_OS_LINUX */

/** @} */

