ApplianceImplIO.cpp revision cd6f71bc352f550074f1ba2c830a2cf2f0b3dd46
/* $Id$ */
/** @file
*
* IO helper for IAppliance COM class implementations.
*/
/*
* Copyright (C) 2010 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*/
/******************************************************************************
* Header Files *
******************************************************************************/
#include "ProgressImpl.h"
#include "ApplianceImpl.h"
#include "ApplianceImplPrivate.h"
/******************************************************************************
* Structures and Typedefs *
******************************************************************************/
typedef struct RTFILESTORAGEINTERNAL
{
/** File handle. */
/** Completion callback. */
typedef struct RTTARSTORAGEINTERNAL
{
/** Tar handle. */
/** Completion callback. */
typedef struct SHA1STORAGEINTERNAL
{
/** Completion callback. */
/** Storage handle for the next callback in chain. */
void *pvStorage;
/** Current file open mode. */
/** Our own storage handle. */
/** Current real position in the file. */
/** Handle of the worker thread. */
/** Status of the worker thread. */
/** Event for signaling a new status. */
/** Event for signaling a finished task of the worker thread. */
/** SHA1 calculation context. */
/** Write mode only: Memory buffer for writing zeros. */
void *pvZeroBuf;
/** Write mode only: Size of the zero memory buffer. */
/** Read mode only: Indicate if we reached end of file. */
volatile bool fEOF;
// uint64_t calls;
// uint64_t waits;
/******************************************************************************
* Defined Constants And Macros *
******************************************************************************/
#define STATUS_WAIT UINT32_C(0)
/* Enable for getting some flow history. */
#if 0
#else
# define DEBUG_PRINT_FLOW() do {} while(0)
#endif
/******************************************************************************
* Internal Functions *
******************************************************************************/
/******************************************************************************
* Internal: RTFile interface
******************************************************************************/
{
/* Validate input. */
if (!pInt)
return VERR_NO_MEMORY;
if (RT_FAILURE(rc))
else
return rc;
}
{
/* Validate input. */
/* Cleanup */
return rc;
}
{
return RTFileDelete(pcszFilename);
}
static int rtFileMoveCallback(void * /* pvUser */, const char *pcszSrc, const char *pcszDst, unsigned fMove)
{
}
static int rtFileGetFreeSpaceCallback(void * /* pvUser */, const char *pcszFilename, int64_t *pcbFreeSpace)
{
/* Validate input. */
return VERR_NOT_IMPLEMENTED;
}
static int rtFileGetModificationTimeCallback(void * /* pvUser */, const char *pcszFilename, PRTTIMESPEC pModificationTime)
{
/* Validate input. */
return VERR_NOT_IMPLEMENTED;
}
{
/* Validate input. */
}
{
/* Validate input. */
}
{
/* Validate input. */
}
{
/* Validate input. */
// DEBUG_PRINT_FLOW();
}
{
/* Validate input. */
}
/******************************************************************************
* Internal: RTTar interface
******************************************************************************/
{
/* Validate input. */
// AssertReturn(!(fOpen & RTFILE_O_READWRITE), VERR_INVALID_PARAMETER);
if (!pInt)
return VERR_NO_MEMORY;
int rc = VINF_SUCCESS;
if ( fOpen & RTFILE_O_READ
&& !(fOpen & RTFILE_O_WRITE))
{
/* Read only is a little bit more complicated than writing, cause we
* need streaming functionality. First try to open the file on the
* current file position. If this is the file the caller requested, we
* are fine. If not seek to the next file in the stream and check
* again. This is repeated until EOF of the OVA. */
/*
*
*
* TODO: recheck this with more VDMKs (or what else) in an test OVA.
*
*
*/
bool fFound = false;
for(;;)
{
char *pszFilename = 0;
if (RT_SUCCESS(rc))
{
if (fFound)
break;
else
{
if (RT_FAILURE(rc))
break;
}
}else
break;
}
if (fFound)
}
else
if (RT_FAILURE(rc))
else
return rc;
}
{
/* Validate input. */
/* Cleanup */
return rc;
}
{
/* Validate input. */
return VERR_NOT_IMPLEMENTED;
}
static int rtTarMoveCallback(void *pvUser, const char *pcszSrc, const char *pcszDst, unsigned /* fMove */)
{
/* Validate input. */
return VERR_NOT_IMPLEMENTED;
}
{
/* Validate input. */
return VERR_NOT_IMPLEMENTED;
}
static int rtTarGetModificationTimeCallback(void *pvUser, const char *pcszFilename, PRTTIMESPEC pModificationTime)
{
/* Validate input. */
return VERR_NOT_IMPLEMENTED;
}
{
/* Validate input. */
}
{
/* Validate input. */
}
{
/* Validate input. */
}
{
/* Validate input. */
}
{
/* Validate input. */
return VERR_NOT_IMPLEMENTED;
}
/******************************************************************************
* Internal: Sha1 interface
******************************************************************************/
{
/* Validate input. */
int rc = VINF_SUCCESS;
bool fLoop = true;
while(fLoop)
{
/* What should we do next? */
// RTPrintf("status: %d\n", u32Status);
switch (u32Status)
{
case STATUS_WAIT:
{
/* Wait for new work. */
if ( RT_FAILURE(rc)
&& rc != VERR_TIMEOUT)
fLoop = false;
break;
}
case STATUS_WRITE:
{
size_t cbMemAllRead = 0;
bool fStop = false;
bool fEOF = false;
/* First loop over all the free memory in the circular
* memory buffer (could be turn around at the end). */
for(;;)
{
if ( cbMemAllRead == cbAvail
|| fStop == true)
break;
char *pcBuf;
/* Try to acquire all the used space of the circular buffer. */
size_t cbAllWritten = 0;
/* Second, write as long as used memory is there. The write
* method could also split the writes up into to smaller
* parts. */
for(;;)
{
if (cbAllWritten == cbMemRead)
break;
rc = pCallbacks->pfnWriteSync(pIO->pvUser, pInt->pvStorage, pInt->cbCurFile, &pcBuf[cbAllWritten], cbToWrite, &cbWritten);
// RTPrintf ("%lu %lu %lu %Rrc\n", pInt->cbCurFile, cbToRead, cbRead, rc);
if (RT_FAILURE(rc))
{
fStop = true;
fLoop = false;
break;
}
if (cbWritten == 0)
{
fStop = true;
fLoop = false;
fEOF = true;
// RTPrintf("EOF\n");
break;
}
}
/* Update the SHA1 context with the next data block. */
/* Mark the block as empty. */
}
/* Reset the thread status and signal the main thread that we
* are finished. Use CmpXchg, so we not overwrite other states
* which could be signaled in the meantime. */
break;
}
case STATUS_READ:
{
size_t cbMemAllWrite = 0;
bool fStop = false;
bool fEOF = false;
/* First loop over all the available memory in the circular
* memory buffer (could be turn around at the end). */
for(;;)
{
if ( cbMemAllWrite == cbAvail
|| fStop == true)
break;
char *pcBuf;
size_t cbMemWrite = 0;
/* Try to acquire all the free space of the circular buffer. */
/* Second, read as long as we filled all the memory. The
* read method could also split the reads up into to
* smaller parts. */
for(;;)
{
if (cbAllRead == cbMemWrite)
break;
rc = pCallbacks->pfnReadSync(pIO->pvUser, pInt->pvStorage, pInt->cbCurFile, &pcBuf[cbAllRead], cbToRead, &cbRead);
// RTPrintf ("%lu %lu %lu %Rrc\n", pInt->cbCurFile, cbToRead, cbRead, rc);
if (RT_FAILURE(rc))
{
fStop = true;
fLoop = false;
break;
}
if (cbRead == 0)
{
fStop = true;
fLoop = false;
fEOF = true;
// RTPrintf("EOF\n");
break;
}
}
/* Update the SHA1 context with the next data block. */
/* Mark the block as full. */
}
if (fEOF)
/* Reset the thread status and signal the main thread that we
* are finished. Use CmpXchg, so we not overwrite other states
* which could be signaled in the meantime. */
break;
}
case STATUS_END:
{
/* End signaled */
fLoop = false;
break;
}
}
}
return rc;
}
{
}
{
// RTPrintf("start\n");
int rc = VINF_SUCCESS;
for(;;)
{
// RTPrintf(" wait\n");
break;
}
if (rc == VERR_TIMEOUT)
rc = VINF_SUCCESS;
return rc;
}
{
int rc = VINF_SUCCESS;
{
/* Let the write worker thread start immediately. */
if (RT_FAILURE(rc))
return rc;
/* Wait until the write worker thread has finished. */
}
return rc;
}
{
/* Validate input. */
AssertReturn((fOpen & RTFILE_O_READWRITE) != RTFILE_O_READWRITE, VERR_INVALID_PARAMETER); /* No read/write allowed */
if (!pInt)
return VERR_NO_MEMORY;
int rc = VINF_SUCCESS;
do
{
/* Circular buffer in the read case. */
if (RT_FAILURE(rc))
break;
if (fOpen & RTFILE_O_WRITE)
{
/* The zero buffer is used for appending empty parts at the end of the
* file (or our buffer) in setSize or when uOffset in writeSync is
* increased in steps bigger than a byte. */
{
rc = VERR_NO_MEMORY;
break;
}
}
/* Create an event semaphore to indicate a state change for the worker
* thread. */
if (RT_FAILURE(rc))
break;
/* Create an event semaphore to indicate a finished calculation of the
worker thread. */
if (RT_FAILURE(rc))
break;
/* Create the worker thread. */
rc = RTThreadCreate(&pInt->pWorkerThread, sha1CalcWorkerThread, pInt, 0, RTTHREADTYPE_MAIN_HEAVY_WORKER, RTTHREADFLAGS_WAITABLE, "SHA1-Worker");
if (RT_FAILURE(rc))
break;
if (pSha1Storage->fCreateDigest)
/* Create a sha1 context the worker thread will work with. */
/* Open the file. */
if (RT_FAILURE(rc))
break;
if (fOpen & RTFILE_O_READ)
{
/* Immediately let the worker thread start the reading. */
}
}
while(0);
if (RT_FAILURE(rc))
{
if (pInt->pWorkerThread)
{
}
if (pInt->workFinishedEvent)
if (pInt->newStatusEvent)
}
else
return rc;
}
{
/* Validate input. */
int rc = VINF_SUCCESS;
/* Make sure all pending writes are flushed */
if (pInt->pWorkerThread)
{
/* Signal the worker thread to end himself */
/* Worker thread stopped? */
}
if ( RT_SUCCESS(rc)
{
/* Finally calculate & format the SHA1 sum */
unsigned char auchDig[RTSHA1_HASH_SIZE];
char *pszDigest;
if (RT_SUCCESS(rc))
{
if (RT_SUCCESS(rc))
}
}
/* Close the file */
// RTPrintf("%lu %lu\n", pInt->calls, pInt->waits);
/* Cleanup */
if (pInt->workFinishedEvent)
if (pInt->newStatusEvent)
return rc;
}
{
/* Validate input. */
}
{
/* Validate input. */
}
{
/* Validate input. */
}
static int sha1GetModificationTimeCallback(void *pvUser, const char *pcszFilename, PRTTIMESPEC pModificationTime)
{
/* Validate input. */
}
{
/* Validate input. */
if (RT_FAILURE(rc))
return rc;
return VINF_SUCCESS;
}
{
/* Validate input. */
}
{
/* Validate input. */
/* Check that the write is linear */
AssertMsgReturn(pInt->cbCurAll <= uOffset, ("Backward seeking is not allowed (uOffset: %7lu cbCurAll: %7lu)!", uOffset, pInt->cbCurAll), VERR_INVALID_PARAMETER);
int rc = VINF_SUCCESS;
/* Check if we have to add some free space at the end, before we start the
* real write. */
{
size_t cbAllWritten = 0;
for(;;)
{
/* Finished? */
if (cbAllWritten == cbSize)
break;
if (RT_FAILURE(rc))
break;
}
if (RT_FAILURE(rc))
return rc;
}
// RTPrintf("Write uOffset: %7lu cbWrite: %7lu = %7lu\n", uOffset, cbWrite, uOffset + cbWrite);
size_t cbAllWritten = 0;
for(;;)
{
/* Finished? */
if (cbAllWritten == cbWrite)
break;
if ( cbAvail == 0
return VERR_EOF;
/* If there isn't enough free space make sure the worker thread is
* writing some data. */
{
if(RT_FAILURE(rc))
break;
/* If there is _no_ free space available, we have to wait until it is. */
if (cbAvail == 0)
{
if (RT_FAILURE(rc))
break;
// RTPrintf("############## wait %lu %lu %lu \n", cbRead, cbAllRead, cbAvail);
// pInt->waits++;
}
}
char *pcBuf;
size_t cbMemWritten = 0;
/* Acquire a block for writing from our circular buffer. */
/* Mark the block full. */
}
if (pcbWritten)
/* Signal the thread to write more data in the mean time. */
if ( RT_SUCCESS(rc)
return rc;
}
{
/* Validate input. */
int rc = VINF_SUCCESS;
// pInt->calls++;
// RTPrintf("Read uOffset: %7lu cbRead: %7lu = %7lu\n", uOffset, cbRead, uOffset + cbRead);
/* Check if we jump forward in the file. If so we have to read the
* remaining stuff in the gap anyway (SHA1; streaming). */
{
if (RT_FAILURE(rc))
return rc;
}
for(;;)
{
/* Finished? */
break;
if ( cbAvail == 0
return VERR_EOF;
/* If there isn't enough data make sure the worker thread is fetching
* more. */
{
if(RT_FAILURE(rc))
break;
/* If there is _no_ data available, we have to wait until it is. */
if (cbAvail == 0)
{
if (RT_FAILURE(rc))
break;
// RTPrintf("############## wait %lu %lu %lu \n", cbRead, cbAllRead, cbAvail);
// pInt->waits++;
}
}
char *pcBuf;
/* Acquire a block for reading from our circular buffer. */
if (pvBuf) /* Make it possible to blind read data (for skipping) */
/* Mark the block as empty again. */
}
if (pcbRead)
rc = VINF_SUCCESS;
/* Signal the thread to read more data in the mean time. */
if ( RT_SUCCESS(rc)
return rc;
}
{
/* Validate input. */
/* Check if there is still something in the buffer. If yes, flush it. */
if (RT_FAILURE(rc))
return rc;
}
/******************************************************************************
* Public Functions *
******************************************************************************/
{
if (!pCallbacks)
return NULL;
return pCallbacks;
}
{
if (!pCallbacks)
return NULL;
return pCallbacks;
}
{
if (!pCallbacks)
return NULL;
return pCallbacks;
}
int Sha1ReadBuf(const char *pcszFilename, void **ppvBuf, size_t *pcbSize, PVDINTERFACEIO pCallbacks, void *pvUser)
{
/* Validate input. */
void *pvStorage;
&pvStorage);
if (RT_FAILURE(rc))
return rc;
void *pvBuf = 0;
do
{
if (RT_FAILURE(rc))
break;
if (!pvBuf)
{
rc = VERR_NO_MEMORY;
break;
}
for(;;)
{
break;
rc = pCallbacks->pfnReadSync(pvUser, pvStorage, cbAllRead, &((char*)pvBuf)[cbAllRead], cbToRead, &cbRead);
if (RT_FAILURE(rc))
break;
}
}while(0);
if (RT_SUCCESS(rc))
{
}else
{
if (pvBuf)
}
return rc;
}
int Sha1WriteBuf(const char *pcszFilename, void *pvBuf, size_t cbSize, PVDINTERFACEIO pCallbacks, void *pvUser)
{
/* Validate input. */
void *pvStorage;
&pvStorage);
if (RT_FAILURE(rc))
return rc;
size_t cbAllWritten = 0;
for(;;)
{
if (cbAllWritten >= cbSize)
break;
rc = pCallbacks->pfnWriteSync(pvUser, pvStorage, cbAllWritten, &((char*)pvBuf)[cbAllWritten], cbToWrite, &cbWritten);
if (RT_FAILURE(rc))
break;
}
return rc;
}