/* $Id: ApplianceImplIO.cpp 57372 2015-08-14 22:01:25Z vboxsync $ */ /** @file * IO helper for IAppliance COM class implementations. */ /* * Copyright (C) 2010-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; * you can redistribute it and/or modify it under the terms of the GNU * 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" #include "VirtualBoxImpl.h" #include #include #include #include #include #include #include #include #include #include #include #include "Logging.h" /********************************************************************************************************************************* * Structures and Typedefs * *********************************************************************************************************************************/ typedef struct FILESTORAGEINTERNAL { /** File handle. */ RTFILE file; /** Completion callback. */ PFNVDCOMPLETED pfnCompleted; } FILESTORAGEINTERNAL, *PFILESTORAGEINTERNAL; typedef struct TARSTORAGEINTERNAL { /** Tar handle. */ RTTARFILE hTarFile; /** Completion callback. */ PFNVDCOMPLETED pfnCompleted; } TARSTORAGEINTERNAL, *PTARSTORAGEINTERNAL; typedef struct SHASTORAGEINTERNAL { /** Completion callback. */ PFNVDCOMPLETED pfnCompleted; /** Storage handle for the next callback in chain. */ void *pvStorage; /** Current file open mode. */ uint32_t fOpenMode; /** Our own storage handle. */ PSHASTORAGE pShaStorage; /** Circular buffer used for transferring data from/to the worker thread. */ PRTCIRCBUF pCircBuf; /** Current absolute position (regardless of the real read/written data). */ uint64_t cbCurAll; /** Current real position in the file. */ uint64_t cbCurFile; /** Handle of the worker thread. */ RTTHREAD pWorkerThread; /** Status of the worker thread. */ volatile uint32_t u32Status; /** Event for signaling a new status. */ RTSEMEVENT newStatusEvent; /** Event for signaling a finished task of the worker thread. */ RTSEMEVENT workFinishedEvent; /** SHA1/SHA256 calculation context. */ union { RTSHA1CONTEXT Sha1; RTSHA256CONTEXT Sha256; } ctx; /** Write mode only: Memory buffer for writing zeros. */ void *pvZeroBuf; /** Write mode only: Size of the zero memory buffer. */ size_t cbZeroBuf; /** Read mode only: Indicate if we reached end of file. */ volatile bool fEOF; // uint64_t calls; // uint64_t waits; } SHASTORAGEINTERNAL, *PSHASTORAGEINTERNAL; /********************************************************************************************************************************* * Defined Constants And Macros * *********************************************************************************************************************************/ #define STATUS_WAIT UINT32_C(0) #define STATUS_WRITE UINT32_C(1) #define STATUS_WRITING UINT32_C(2) #define STATUS_READ UINT32_C(3) #define STATUS_READING UINT32_C(4) #define STATUS_END UINT32_C(5) /* Enable for getting some flow history. */ #if 0 # define DEBUG_PRINT_FLOW() RTPrintf("%s\n", __FUNCTION__) #else # define DEBUG_PRINT_FLOW() do {} while (0) #endif /********************************************************************************************************************************* * Internal Functions * *********************************************************************************************************************************/ /** @name VDINTERFACEIO stubs returning not-implemented. * @{ */ /** @interface_method_impl{VDINTERFACEIO,pfnDelete} */ static DECLCALLBACK(int) notImpl_Delete(void *pvUser, const char *pcszFilename) { NOREF(pvUser); NOREF(pcszFilename); Log(("%s\n", __FUNCTION__)); DEBUG_PRINT_FLOW(); return VERR_NOT_IMPLEMENTED; } /** @interface_method_impl{VDINTERFACEIO,pfnMove} */ static DECLCALLBACK(int) notImpl_Move(void *pvUser, const char *pcszSrc, const char *pcszDst, unsigned fMove) { NOREF(pvUser); NOREF(pcszSrc); NOREF(pcszDst); NOREF(fMove); Log(("%s\n", __FUNCTION__)); DEBUG_PRINT_FLOW(); return VERR_NOT_IMPLEMENTED; } /** @interface_method_impl{VDINTERFACEIO,pfnGetFreeSpace} */ static DECLCALLBACK(int) notImpl_GetFreeSpace(void *pvUser, const char *pcszFilename, int64_t *pcbFreeSpace) { NOREF(pvUser); NOREF(pcszFilename); NOREF(pcbFreeSpace); Log(("%s\n", __FUNCTION__)); DEBUG_PRINT_FLOW(); return VERR_NOT_IMPLEMENTED; } /** @interface_method_impl{VDINTERFACEIO,pfnGetModificationTime} */ static DECLCALLBACK(int) notImpl_GetModificationTime(void *pvUser, const char *pcszFilename, PRTTIMESPEC pModificationTime) { NOREF(pvUser); NOREF(pcszFilename); NOREF(pModificationTime); Log(("%s\n", __FUNCTION__)); DEBUG_PRINT_FLOW(); return VERR_NOT_IMPLEMENTED; } /** @interface_method_impl{VDINTERFACEIO,pfnSetSize} */ static DECLCALLBACK(int) notImpl_SetSize(void *pvUser, void *pvStorage, uint64_t cb) { NOREF(pvUser); NOREF(pvStorage); NOREF(cb); Log(("%s\n", __FUNCTION__)); DEBUG_PRINT_FLOW(); return VERR_NOT_IMPLEMENTED; } /** @interface_method_impl{VDINTERFACEIO,pfnWriteSync} */ static DECLCALLBACK(int) notImpl_WriteSync(void *pvUser, void *pvStorage, uint64_t off, const void *pvBuf, size_t cbWrite, size_t *pcbWritten) { NOREF(pvUser); NOREF(pvStorage); NOREF(off); NOREF(pvBuf); NOREF(cbWrite); NOREF(pcbWritten); Log(("%s\n", __FUNCTION__)); DEBUG_PRINT_FLOW(); return VERR_NOT_IMPLEMENTED; } /** @interface_method_impl{VDINTERFACEIO,pfnFlushSync} */ static DECLCALLBACK(int) notImpl_FlushSync(void *pvUser, void *pvStorage) { NOREF(pvUser); NOREF(pvStorage); Log(("%s\n", __FUNCTION__)); DEBUG_PRINT_FLOW(); return VERR_NOT_IMPLEMENTED; } /** @} */ /****************************************************************************** * Internal: RTFile interface ******************************************************************************/ static int fileOpenCallback(void * /* pvUser */, const char *pszLocation, uint32_t fOpen, PFNVDCOMPLETED pfnCompleted, void **ppInt) { /* Validate input. */ AssertPtrReturn(ppInt, VERR_INVALID_POINTER); AssertPtrNullReturn(pfnCompleted, VERR_INVALID_PARAMETER); DEBUG_PRINT_FLOW(); PFILESTORAGEINTERNAL pInt = (PFILESTORAGEINTERNAL)RTMemAllocZ(sizeof(FILESTORAGEINTERNAL)); if (!pInt) return VERR_NO_MEMORY; pInt->pfnCompleted = pfnCompleted; int rc = RTFileOpen(&pInt->file, pszLocation, fOpen); if (RT_FAILURE(rc)) RTMemFree(pInt); else *ppInt = pInt; return rc; } static int fileCloseCallback(void * /* pvUser */, void *pvStorage) { /* Validate input. */ AssertPtrReturn(pvStorage, VERR_INVALID_POINTER); PFILESTORAGEINTERNAL pInt = (PFILESTORAGEINTERNAL)pvStorage; DEBUG_PRINT_FLOW(); int rc = RTFileClose(pInt->file); /* Cleanup */ RTMemFree(pInt); return rc; } static int fileDeleteCallback(void * /* pvUser */, const char *pcszFilename) { DEBUG_PRINT_FLOW(); return RTFileDelete(pcszFilename); } static int fileMoveCallback(void * /* pvUser */, const char *pcszSrc, const char *pcszDst, unsigned fMove) { DEBUG_PRINT_FLOW(); return RTFileMove(pcszSrc, pcszDst, fMove); } static int fileGetFreeSpaceCallback(void * /* pvUser */, const char *pcszFilename, int64_t *pcbFreeSpace) { /* Validate input. */ AssertPtrReturn(pcszFilename, VERR_INVALID_POINTER); AssertPtrReturn(pcbFreeSpace, VERR_INVALID_POINTER); DEBUG_PRINT_FLOW(); return VERR_NOT_IMPLEMENTED; } static int fileGetModificationTimeCallback(void * /* pvUser */, const char *pcszFilename, PRTTIMESPEC pModificationTime) { /* Validate input. */ AssertPtrReturn(pcszFilename, VERR_INVALID_POINTER); AssertPtrReturn(pModificationTime, VERR_INVALID_POINTER); DEBUG_PRINT_FLOW(); return VERR_NOT_IMPLEMENTED; } static int fileGetSizeCallback(void * /* pvUser */, void *pvStorage, uint64_t *pcbSize) { /* Validate input. */ AssertPtrReturn(pvStorage, VERR_INVALID_POINTER); PFILESTORAGEINTERNAL pInt = (PFILESTORAGEINTERNAL)pvStorage; DEBUG_PRINT_FLOW(); return RTFileGetSize(pInt->file, pcbSize); } static int fileSetSizeCallback(void * /* pvUser */, void *pvStorage, uint64_t cbSize) { /* Validate input. */ AssertPtrReturn(pvStorage, VERR_INVALID_POINTER); PFILESTORAGEINTERNAL pInt = (PFILESTORAGEINTERNAL)pvStorage; DEBUG_PRINT_FLOW(); return RTFileSetSize(pInt->file, cbSize); } static int fileWriteSyncCallback(void * /* pvUser */, void *pvStorage, uint64_t uOffset, const void *pvBuf, size_t cbWrite, size_t *pcbWritten) { /* Validate input. */ AssertPtrReturn(pvStorage, VERR_INVALID_POINTER); PFILESTORAGEINTERNAL pInt = (PFILESTORAGEINTERNAL)pvStorage; return RTFileWriteAt(pInt->file, uOffset, pvBuf, cbWrite, pcbWritten); } static int fileReadSyncCallback(void * /* pvUser */, void *pvStorage, uint64_t uOffset, void *pvBuf, size_t cbRead, size_t *pcbRead) { /* Validate input. */ AssertPtrReturn(pvStorage, VERR_INVALID_POINTER); // DEBUG_PRINT_FLOW(); PFILESTORAGEINTERNAL pInt = (PFILESTORAGEINTERNAL)pvStorage; return RTFileReadAt(pInt->file, uOffset, pvBuf, cbRead, pcbRead); } static int fileFlushSyncCallback(void * /* pvUser */, void *pvStorage) { /* Validate input. */ AssertPtrReturn(pvStorage, VERR_INVALID_POINTER); DEBUG_PRINT_FLOW(); PFILESTORAGEINTERNAL pInt = (PFILESTORAGEINTERNAL)pvStorage; return RTFileFlush(pInt->file); } /** @name VDINTERFACEIO implementation that writes TAR files via RTTar. * @{ */ static DECLCALLBACK(int) tarWriter_Open(void *pvUser, const char *pszLocation, uint32_t fOpen, PFNVDCOMPLETED pfnCompleted, void **ppInt) { /* Validate input. */ AssertPtrReturn(pvUser, VERR_INVALID_POINTER); AssertPtrReturn(ppInt, VERR_INVALID_POINTER); AssertPtrNullReturn(pfnCompleted, VERR_INVALID_PARAMETER); AssertReturn(fOpen & RTFILE_O_WRITE, VERR_INVALID_PARAMETER); /* Only for writing. */ DEBUG_PRINT_FLOW(); /* * Allocate a storage handle. */ int rc; PTARSTORAGEINTERNAL pInt = (PTARSTORAGEINTERNAL)RTMemAllocZ(sizeof(TARSTORAGEINTERNAL)); if (pInt) { pInt->pfnCompleted = pfnCompleted; /* * Try open the file. */ rc = RTTarFileOpen((RTTAR)pvUser, &pInt->hTarFile, RTPathFilename(pszLocation), fOpen); if (RT_SUCCESS(rc)) *ppInt = pInt; else RTMemFree(pInt); } else rc = VERR_NO_MEMORY; return rc; } static DECLCALLBACK(int) tarWriter_Close(void *pvUser, void *pvStorage) { /* Validate input. */ AssertPtrReturn(pvUser, VERR_INVALID_POINTER); AssertPtrReturn(pvStorage, VERR_INVALID_POINTER); PTARSTORAGEINTERNAL pInt = (PTARSTORAGEINTERNAL)pvStorage; DEBUG_PRINT_FLOW(); int rc = RTTarFileClose(pInt->hTarFile); pInt->hTarFile = NIL_RTTARFILE; /* Cleanup */ RTMemFree(pInt); return rc; } static DECLCALLBACK(int) tarWriter_GetSize(void *pvUser, void *pvStorage, uint64_t *pcbSize) { /** @todo Not sure if this is really required, but it's not a biggie to keep * around. */ /* Validate input. */ AssertPtrReturn(pvUser, VERR_INVALID_POINTER); AssertPtrReturn(pvStorage, VERR_INVALID_POINTER); PTARSTORAGEINTERNAL pInt = (PTARSTORAGEINTERNAL)pvStorage; DEBUG_PRINT_FLOW(); return RTTarFileGetSize(pInt->hTarFile, pcbSize); } static DECLCALLBACK(int) tarWriter_SetSize(void *pvUser, void *pvStorage, uint64_t cbSize) { /* Validate input. */ AssertPtrReturn(pvUser, VERR_INVALID_POINTER); AssertPtrReturn(pvStorage, VERR_INVALID_POINTER); PTARSTORAGEINTERNAL pInt = (PTARSTORAGEINTERNAL)pvStorage; DEBUG_PRINT_FLOW(); return RTTarFileSetSize(pInt->hTarFile, cbSize); } static DECLCALLBACK(int) tarWriter_WriteSync(void *pvUser, void *pvStorage, uint64_t uOffset, const void *pvBuf, size_t cbWrite, size_t *pcbWritten) { /* Validate input. */ AssertPtrReturn(pvUser, VERR_INVALID_POINTER); AssertPtrReturn(pvStorage, VERR_INVALID_POINTER); PTARSTORAGEINTERNAL pInt = (PTARSTORAGEINTERNAL)pvStorage; DEBUG_PRINT_FLOW(); return RTTarFileWriteAt(pInt->hTarFile, uOffset, pvBuf, cbWrite, pcbWritten); } static DECLCALLBACK(int) tarWriter_ReadSync(void *pvUser, void *pvStorage, uint64_t uOffset, void *pvBuf, size_t cbRead, size_t *pcbRead) { /** @todo Not sure if this is really required, but it's not a biggie to keep * around. */ /* Validate input. */ AssertPtrReturn(pvUser, VERR_INVALID_POINTER); AssertPtrReturn(pvStorage, VERR_INVALID_POINTER); PTARSTORAGEINTERNAL pInt = (PTARSTORAGEINTERNAL)pvStorage; // DEBUG_PRINT_FLOW(); return RTTarFileReadAt(pInt->hTarFile, uOffset, pvBuf, cbRead, pcbRead); } PVDINTERFACEIO tarWriterCreateInterface(void) { PVDINTERFACEIO pCallbacks = (PVDINTERFACEIO)RTMemAllocZ(sizeof(VDINTERFACEIO)); if (!pCallbacks) return NULL; pCallbacks->pfnOpen = tarWriter_Open; pCallbacks->pfnClose = tarWriter_Close; pCallbacks->pfnDelete = notImpl_Delete; pCallbacks->pfnMove = notImpl_Move; pCallbacks->pfnGetFreeSpace = notImpl_GetFreeSpace; pCallbacks->pfnGetModificationTime = notImpl_GetModificationTime; pCallbacks->pfnGetSize = tarWriter_GetSize; pCallbacks->pfnSetSize = tarWriter_SetSize; pCallbacks->pfnReadSync = tarWriter_ReadSync; pCallbacks->pfnWriteSync = tarWriter_WriteSync; pCallbacks->pfnFlushSync = notImpl_FlushSync; return pCallbacks; } /** @} */ /** @name VDINTERFACEIO implementation on top of an IPRT file system stream. * @{ */ /** * Internal data for read only I/O stream (related to FSSRDONLYINTERFACEIO). */ typedef struct IOSRDONLYINTERNAL { /** The I/O stream. */ RTVFSIOSTREAM hVfsIos; /** Completion callback. */ PFNVDCOMPLETED pfnCompleted; } IOSRDONLYINTERNAL, *PIOSRDONLYINTERNAL; /** * Extended VD I/O interface structure that fssRdOnly uses. * * It's passed as pvUser to each call. */ typedef struct FSSRDONLYINTERFACEIO { VDINTERFACEIO CoreIo; /** The file system stream object. */ RTVFSFSSTREAM hVfsFss; /** Set if we've seen VERR_EOF on the file system stream already. */ bool fEndOfFss; /** The current object in the stream. */ RTVFSOBJ hVfsCurObj; /** The name of the current object. */ char *pszCurName; /** The type of the current object. */ RTVFSOBJTYPE enmCurType; } FSSRDONLYINTERFACEIO; /** @interface_method_impl{VDINTERFACEIO,pfnOpen} */ static DECLCALLBACK(int) fssRdOnly_Open(void *pvUser, const char *pszLocation, uint32_t fOpen, PFNVDCOMPLETED pfnCompleted, void **ppInt) { PFSSRDONLYINTERFACEIO pThis = (PFSSRDONLYINTERFACEIO)pvUser; /* * Validate input. */ AssertPtrReturn(ppInt, VERR_INVALID_POINTER); AssertPtrNullReturn(pfnCompleted, VERR_INVALID_PARAMETER); AssertReturn((fOpen & RTFILE_O_ACCESS_MASK) == RTFILE_O_READ, VERR_INVALID_PARAMETER); DEBUG_PRINT_FLOW(); /* * Scan the stream until a matching file is found. */ for (;;) { if (pThis->hVfsCurObj != NIL_RTVFSOBJ) { if (RTStrICmp(pThis->pszCurName, pszLocation) == 0) { switch (pThis->enmCurType) { case RTVFSOBJTYPE_IO_STREAM: case RTVFSOBJTYPE_FILE: { PIOSRDONLYINTERNAL pFile = (PIOSRDONLYINTERNAL)RTMemAlloc(sizeof(*pFile)); if (!pFile) return VERR_NO_MEMORY; pFile->hVfsIos = RTVfsObjToIoStream(pThis->hVfsCurObj); pFile->pfnCompleted = pfnCompleted; *ppInt = pFile; /* Force stream to be advanced on next open call. */ RTVfsObjRelease(pThis->hVfsCurObj); pThis->hVfsCurObj = NIL_RTVFSOBJ; RTStrFree(pThis->pszCurName); pThis->pszCurName = NULL; return VINF_SUCCESS; } case RTVFSOBJTYPE_DIR: return VERR_IS_A_DIRECTORY; default: return VERR_UNEXPECTED_FS_OBJ_TYPE; } } /* * Drop the current stream object. */ RTVfsObjRelease(pThis->hVfsCurObj); pThis->hVfsCurObj = NIL_RTVFSOBJ; RTStrFree(pThis->pszCurName); pThis->pszCurName = NULL; } /* * Fetch the next object in the stream. */ if (pThis->fEndOfFss) return VERR_FILE_NOT_FOUND; int rc = RTVfsFsStrmNext(pThis->hVfsFss, &pThis->pszCurName, &pThis->enmCurType, &pThis->hVfsCurObj); if (RT_FAILURE(rc)) { pThis->fEndOfFss = rc == VERR_EOF; return rc == VERR_EOF ? VERR_FILE_NOT_FOUND : rc; } } } /** @interface_method_impl{VDINTERFACEIO,pfnClose} */ static int fssRdOnly_Close(void *pvUser, void *pvStorage) { PIOSRDONLYINTERNAL pFile = (PIOSRDONLYINTERNAL)pvStorage; AssertPtrReturn(pvUser, VERR_INVALID_POINTER); AssertPtrReturn(pFile, VERR_INVALID_POINTER); DEBUG_PRINT_FLOW(); uint32_t cRefs = RTVfsIoStrmRelease(pFile->hVfsIos); pFile->hVfsIos = NIL_RTVFSIOSTREAM; RTMemFree(pFile); return cRefs != UINT32_MAX ? VINF_SUCCESS : VERR_INTERNAL_ERROR_3; } /** @interface_method_impl{VDINTERFACEIO,pfnGetSize} */ static DECLCALLBACK(int) fssRdOnly_GetSize(void *pvUser, void *pvStorage, uint64_t *pcb) { PIOSRDONLYINTERNAL pFile = (PIOSRDONLYINTERNAL)pvStorage; AssertPtrReturn(pvUser, VERR_INVALID_POINTER); AssertPtrReturn(pFile, VERR_INVALID_POINTER); AssertPtrReturn(pcb, VERR_INVALID_POINTER); DEBUG_PRINT_FLOW(); RTFSOBJINFO ObjInfo; int rc = RTVfsIoStrmQueryInfo(pFile->hVfsIos, &ObjInfo, RTFSOBJATTRADD_NOTHING); if (RT_SUCCESS(rc)) *pcb = ObjInfo.cbObject; return rc; } /** @interface_method_impl{VDINTERFACEIO,pfnRead} */ static DECLCALLBACK(int) fssRdOnly_ReadSync(void *pvUser, void *pvStorage, uint64_t off, void *pvBuf, size_t cbToRead, size_t *pcbRead) { PIOSRDONLYINTERNAL pFile = (PIOSRDONLYINTERNAL)pvStorage; AssertPtrReturn(pvUser, VERR_INVALID_POINTER); AssertPtrReturn(pFile, VERR_INVALID_POINTER); AssertPtrNullReturn(pcbRead, VERR_INVALID_POINTER); DEBUG_PRINT_FLOW(); return RTVfsIoStrmReadAt(pFile->hVfsIos, off, pvBuf, cbToRead, true /*fBlocking*/, pcbRead); } /** * Opens the specified tar file for stream-like reading, returning a VD I/O * interface to it. * * @returns VBox status code. * @param pszFilename The path to the TAR file. * @param ppTarIo Where to return the VD I/O interface. This * shall be passed as pvUser when using the * interface. * * Pass to fssRdOnlyDestroyInterface for cleaning * up! */ int fssRdOnlyCreateInterfaceForTarFile(const char *pszFilename, PFSSRDONLYINTERFACEIO *ppTarIo) { /* * Open the tar file first. */ RTVFSFILE hVfsFile; int rc = RTVfsFileOpenNormal(pszFilename, RTFILE_O_READ | RTFILE_O_DENY_NONE | RTFILE_O_OPEN, &hVfsFile); if (RT_SUCCESS(rc)) { RTVFSIOSTREAM hVfsIos = RTVfsFileToIoStream(hVfsFile); RTVFSFSSTREAM hVfsFss; rc = RTZipTarFsStreamFromIoStream(hVfsIos, 0 /*fFlags*/, &hVfsFss); if (RT_SUCCESS(rc)) { /* * Allocate and init a callback + instance data structure. */ PFSSRDONLYINTERFACEIO pThis = (PFSSRDONLYINTERFACEIO)RTMemAllocZ(sizeof(*pThis)); if (pThis) { RTVfsIoStrmRelease(hVfsIos); RTVfsFileRelease(hVfsFile); pThis->CoreIo.pfnOpen = fssRdOnly_Open; pThis->CoreIo.pfnClose = fssRdOnly_Close; pThis->CoreIo.pfnDelete = notImpl_Delete; pThis->CoreIo.pfnMove = notImpl_Move; pThis->CoreIo.pfnGetFreeSpace = notImpl_GetFreeSpace; pThis->CoreIo.pfnGetModificationTime = notImpl_GetModificationTime; pThis->CoreIo.pfnGetSize = fssRdOnly_GetSize; pThis->CoreIo.pfnSetSize = notImpl_SetSize; pThis->CoreIo.pfnReadSync = fssRdOnly_ReadSync; pThis->CoreIo.pfnWriteSync = notImpl_WriteSync; pThis->CoreIo.pfnFlushSync = notImpl_FlushSync; pThis->hVfsFss = hVfsFss; pThis->fEndOfFss = false; pThis->hVfsCurObj = NIL_RTVFSOBJ; pThis->pszCurName = NULL; pThis->enmCurType = RTVFSOBJTYPE_INVALID; *ppTarIo = pThis; return VINF_SUCCESS; } RTVfsFsStrmRelease(hVfsFss); } RTVfsIoStrmRelease(hVfsIos); RTVfsFileRelease(hVfsFile); } *ppTarIo = NULL; return rc; } /** * Destroys a read-only FSS interface. * * @param pFssIo What TarFssCreateReadOnlyInterfaceForFile * returned. */ void fssRdOnlyDestroyInterface(PFSSRDONLYINTERFACEIO pFssIo) { AssertPtr(pFssIo); AssertPtr(pFssIo->hVfsFss); RTVfsFsStrmRelease(pFssIo->hVfsFss); pFssIo->hVfsFss = NIL_RTVFSFSSTREAM; RTVfsObjRelease(pFssIo->hVfsCurObj); pFssIo->hVfsCurObj = NIL_RTVFSOBJ; RTStrFree(pFssIo->pszCurName); pFssIo->pszCurName = NULL; RTMemFree(pFssIo); } /** * Returns the read-only name of the current stream object. * * @returns VBox status code. * @param pFssIo What TarFssCreateReadOnlyInterfaceForFile * returned. * @param ppszName Where to return the filename. DO NOT FREE! */ int fssRdOnlyGetCurrentName(PFSSRDONLYINTERFACEIO pFssIo, const char **ppszName) { AssertPtr(pFssIo); AssertPtr(pFssIo->hVfsFss); if (pFssIo->hVfsCurObj == NIL_RTVFSOBJ) { if (pFssIo->fEndOfFss) return VERR_EOF; int rc = RTVfsFsStrmNext(pFssIo->hVfsFss, &pFssIo->pszCurName, &pFssIo->enmCurType, &pFssIo->hVfsCurObj); if (RT_FAILURE(rc)) { pFssIo->fEndOfFss = rc == VERR_EOF; *ppszName = NULL; return rc; } } *ppszName = pFssIo->pszCurName; return VINF_SUCCESS; } /** * Skips the current object. * * @returns VBox status code. * @param pFssIo What TarFssCreateReadOnlyInterfaceForFile * returned. */ int fssRdOnlySkipCurrent(PFSSRDONLYINTERFACEIO pFssIo) { AssertPtr(pFssIo); AssertPtr(pFssIo->hVfsFss); if (pFssIo->hVfsCurObj == NIL_RTVFSOBJ) { if (pFssIo->fEndOfFss) return VERR_EOF; int rc = RTVfsFsStrmNext(pFssIo->hVfsFss, &pFssIo->pszCurName, &pFssIo->enmCurType, &pFssIo->hVfsCurObj); if (RT_FAILURE(rc)) { pFssIo->fEndOfFss = rc == VERR_EOF; return rc; } } /* Force a RTVfsFsStrmNext call the next time around. */ RTVfsObjRelease(pFssIo->hVfsCurObj); pFssIo->hVfsCurObj = NIL_RTVFSOBJ; RTStrFree(pFssIo->pszCurName); pFssIo->pszCurName = NULL; return VINF_SUCCESS; } /** * Checks if the current file is a directory. * * @returns true if directory, false if not (or error). * @param pFssIo What TarFssCreateReadOnlyInterfaceForFile * returned. */ bool fssRdOnlyIsCurrentDirectory(PFSSRDONLYINTERFACEIO pFssIo) { AssertPtr(pFssIo); AssertPtr(pFssIo->hVfsFss); if (pFssIo->hVfsCurObj == NIL_RTVFSOBJ) { if (pFssIo->fEndOfFss) return false; int rc = RTVfsFsStrmNext(pFssIo->hVfsFss, &pFssIo->pszCurName, &pFssIo->enmCurType, &pFssIo->hVfsCurObj); if (RT_FAILURE(rc)) { pFssIo->fEndOfFss = rc == VERR_EOF; return false; } } return pFssIo->enmCurType == RTVFSOBJTYPE_DIR; } /** @} */ /****************************************************************************** * Internal: RTSha interface ******************************************************************************/ DECLCALLBACK(int) shaCalcWorkerThread(RTTHREAD /* aThread */, void *pvUser) { /* Validate input. */ AssertPtrReturn(pvUser, VERR_INVALID_POINTER); PSHASTORAGEINTERNAL pInt = (PSHASTORAGEINTERNAL)pvUser; PVDINTERFACEIO pIfIo = VDIfIoGet(pInt->pShaStorage->pVDImageIfaces); AssertPtrReturn(pIfIo, VERR_INVALID_PARAMETER); int rc = VINF_SUCCESS; bool fLoop = true; while (fLoop) { /* What should we do next? */ uint32_t u32Status = ASMAtomicReadU32(&pInt->u32Status); // RTPrintf("status: %d\n", u32Status); switch (u32Status) { case STATUS_WAIT: { /* Wait for new work. */ rc = RTSemEventWait(pInt->newStatusEvent, 100); if ( RT_FAILURE(rc) && rc != VERR_TIMEOUT) fLoop = false; break; } case STATUS_WRITE: { ASMAtomicCmpXchgU32(&pInt->u32Status, STATUS_WRITING, STATUS_WRITE); size_t cbAvail = RTCircBufUsed(pInt->pCircBuf); size_t cbMemAllRead = 0; /* First loop over all the free memory in the circular * memory buffer (could be turn around at the end). */ for (;;) { if ( cbMemAllRead == cbAvail || fLoop == false) break; char *pcBuf; size_t cbMemToRead = cbAvail - cbMemAllRead; size_t cbMemRead = 0; /* Try to acquire all the used space of the circular buffer. */ RTCircBufAcquireReadBlock(pInt->pCircBuf, cbMemToRead, (void**)&pcBuf, &cbMemRead); 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; size_t cbToWrite = cbMemRead - cbAllWritten; size_t cbWritten = 0; rc = vdIfIoFileWriteSync(pIfIo, pInt->pvStorage, pInt->cbCurFile, &pcBuf[cbAllWritten], cbToWrite, &cbWritten); // RTPrintf ("%lu %lu %lu %Rrc\n", pInt->cbCurFile, cbToRead, cbRead, rc); if (RT_FAILURE(rc)) { fLoop = false; break; } cbAllWritten += cbWritten; pInt->cbCurFile += cbWritten; } /* Update the SHA1/SHA256 context with the next data block. */ if ( RT_SUCCESS(rc) && pInt->pShaStorage->fCreateDigest) { if (pInt->pShaStorage->fSha256) RTSha256Update(&pInt->ctx.Sha256, pcBuf, cbAllWritten); else RTSha1Update(&pInt->ctx.Sha1, pcBuf, cbAllWritten); } /* Mark the block as empty. */ RTCircBufReleaseReadBlock(pInt->pCircBuf, cbAllWritten); cbMemAllRead += cbAllWritten; } /* 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. */ if (ASMAtomicCmpXchgU32(&pInt->u32Status, STATUS_WAIT, STATUS_WRITING)) rc = RTSemEventSignal(pInt->workFinishedEvent); break; } case STATUS_READ: { ASMAtomicCmpXchgU32(&pInt->u32Status, STATUS_READING, STATUS_READ); size_t cbAvail = RTCircBufFree(pInt->pCircBuf); size_t cbMemAllWrite = 0; /* First loop over all the available memory in the circular * memory buffer (could be turn around at the end). */ for (;;) { if ( cbMemAllWrite == cbAvail || fLoop == false) break; char *pcBuf; size_t cbMemToWrite = cbAvail - cbMemAllWrite; size_t cbMemWrite = 0; /* Try to acquire all the free space of the circular buffer. */ RTCircBufAcquireWriteBlock(pInt->pCircBuf, cbMemToWrite, (void**)&pcBuf, &cbMemWrite); /* Second, read as long as we filled all the memory. The * read method could also split the reads up into to * smaller parts. */ size_t cbAllRead = 0; for (;;) { if (cbAllRead == cbMemWrite) break; size_t cbToRead = cbMemWrite - cbAllRead; size_t cbRead = 0; rc = vdIfIoFileReadSync(pIfIo, pInt->pvStorage, pInt->cbCurFile, &pcBuf[cbAllRead], cbToRead, &cbRead); // RTPrintf ("%lu %lu %lu %Rrc\n", pInt->cbCurFile, cbToRead, cbRead, rc); if (RT_FAILURE(rc)) { fLoop = false; break; } /* This indicates end of file. Stop reading. */ if (cbRead == 0) { fLoop = false; ASMAtomicWriteBool(&pInt->fEOF, true); break; } cbAllRead += cbRead; pInt->cbCurFile += cbRead; } /* Update the SHA1/SHA256 context with the next data block. */ if ( RT_SUCCESS(rc) && pInt->pShaStorage->fCreateDigest) { if (pInt->pShaStorage->fSha256) RTSha256Update(&pInt->ctx.Sha256, pcBuf, cbAllRead); else RTSha1Update(&pInt->ctx.Sha1, pcBuf, cbAllRead); } /* Mark the block as full. */ RTCircBufReleaseWriteBlock(pInt->pCircBuf, cbAllRead); cbMemAllWrite += cbAllRead; } /* 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. */ if (ASMAtomicCmpXchgU32(&pInt->u32Status, STATUS_WAIT, STATUS_READING)) rc = RTSemEventSignal(pInt->workFinishedEvent); break; } case STATUS_END: { /* End signaled */ fLoop = false; break; } } } /* Cleanup any status changes to indicate we are finished. */ ASMAtomicWriteU32(&pInt->u32Status, STATUS_END); rc = RTSemEventSignal(pInt->workFinishedEvent); return rc; } DECLINLINE(int) shaSignalManifestThread(PSHASTORAGEINTERNAL pInt, uint32_t uStatus) { ASMAtomicWriteU32(&pInt->u32Status, uStatus); return RTSemEventSignal(pInt->newStatusEvent); } DECLINLINE(int) shaWaitForManifestThreadFinished(PSHASTORAGEINTERNAL pInt) { // RTPrintf("start\n"); int rc = VINF_SUCCESS; for (;;) { // RTPrintf(" wait\n"); uint32_t u32Status = ASMAtomicReadU32(&pInt->u32Status); if (!( u32Status == STATUS_WRITE || u32Status == STATUS_WRITING || u32Status == STATUS_READ || u32Status == STATUS_READING)) break; rc = RTSemEventWait(pInt->workFinishedEvent, 100); } if (rc == VERR_TIMEOUT) rc = VINF_SUCCESS; return rc; } DECLINLINE(int) shaFlushCurBuf(PSHASTORAGEINTERNAL pInt) { int rc = VINF_SUCCESS; if (pInt->fOpenMode & RTFILE_O_WRITE) { /* Let the write worker thread start immediately. */ rc = shaSignalManifestThread(pInt, STATUS_WRITE); if (RT_FAILURE(rc)) return rc; /* Wait until the write worker thread has finished. */ rc = shaWaitForManifestThreadFinished(pInt); } return rc; } static int shaOpenCallback(void *pvUser, const char *pszLocation, uint32_t fOpen, PFNVDCOMPLETED pfnCompleted, void **ppInt) { /* Validate input. */ AssertPtrReturn(pvUser, VERR_INVALID_PARAMETER); AssertPtrReturn(pszLocation, VERR_INVALID_POINTER); AssertPtrNullReturn(pfnCompleted, VERR_INVALID_PARAMETER); AssertPtrReturn(ppInt, VERR_INVALID_POINTER); AssertReturn((fOpen & RTFILE_O_READWRITE) != RTFILE_O_READWRITE, VERR_INVALID_PARAMETER); /* No read/write allowed */ PSHASTORAGE pShaStorage = (PSHASTORAGE)pvUser; PVDINTERFACEIO pIfIo = VDIfIoGet(pShaStorage->pVDImageIfaces); AssertPtrReturn(pIfIo, VERR_INVALID_PARAMETER); DEBUG_PRINT_FLOW(); PSHASTORAGEINTERNAL pInt = (PSHASTORAGEINTERNAL)RTMemAllocZ(sizeof(SHASTORAGEINTERNAL)); if (!pInt) return VERR_NO_MEMORY; int rc = VINF_SUCCESS; do { pInt->pfnCompleted = pfnCompleted; pInt->pShaStorage = pShaStorage; pInt->fEOF = false; pInt->fOpenMode = fOpen; pInt->u32Status = STATUS_WAIT; /* Circular buffer in the read case. */ rc = RTCircBufCreate(&pInt->pCircBuf, _1M * 2); 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. */ pInt->cbZeroBuf = _1K; pInt->pvZeroBuf = RTMemAllocZ(pInt->cbZeroBuf); if (!pInt->pvZeroBuf) { rc = VERR_NO_MEMORY; break; } } /* Create an event semaphore to indicate a state change for the worker * thread. */ rc = RTSemEventCreate(&pInt->newStatusEvent); if (RT_FAILURE(rc)) break; /* Create an event semaphore to indicate a finished calculation of the worker thread. */ rc = RTSemEventCreate(&pInt->workFinishedEvent); if (RT_FAILURE(rc)) break; /* Create the worker thread. */ rc = RTThreadCreate(&pInt->pWorkerThread, shaCalcWorkerThread, pInt, 0, RTTHREADTYPE_MAIN_HEAVY_WORKER, RTTHREADFLAGS_WAITABLE, "SHA-Worker"); if (RT_FAILURE(rc)) break; if (pShaStorage->fCreateDigest) { /* Create a SHA1/SHA256 context the worker thread will work with. */ if (pShaStorage->fSha256) RTSha256Init(&pInt->ctx.Sha256); else RTSha1Init(&pInt->ctx.Sha1); } /* Open the file. */ rc = vdIfIoFileOpen(pIfIo, pszLocation, fOpen, pInt->pfnCompleted, &pInt->pvStorage); if (RT_FAILURE(rc)) break; if (fOpen & RTFILE_O_READ) { /* Immediately let the worker thread start the reading. */ rc = shaSignalManifestThread(pInt, STATUS_READ); } } while (0); if (RT_FAILURE(rc)) { if (pInt->pWorkerThread) { shaSignalManifestThread(pInt, STATUS_END); RTThreadWait(pInt->pWorkerThread, RT_INDEFINITE_WAIT, 0); } if (pInt->workFinishedEvent) RTSemEventDestroy(pInt->workFinishedEvent); if (pInt->newStatusEvent) RTSemEventDestroy(pInt->newStatusEvent); if (pInt->pCircBuf) RTCircBufDestroy(pInt->pCircBuf); if (pInt->pvZeroBuf) RTMemFree(pInt->pvZeroBuf); RTMemFree(pInt); } else *ppInt = pInt; return rc; } static int shaCloseCallback(void *pvUser, void *pvStorage) { /* Validate input. */ AssertPtrReturn(pvUser, VERR_INVALID_POINTER); AssertPtrReturn(pvStorage, VERR_INVALID_POINTER); PSHASTORAGE pShaStorage = (PSHASTORAGE)pvUser; PVDINTERFACEIO pIfIo = VDIfIoGet(pShaStorage->pVDImageIfaces); AssertPtrReturn(pIfIo, VERR_INVALID_PARAMETER); PSHASTORAGEINTERNAL pInt = (PSHASTORAGEINTERNAL)pvStorage; DEBUG_PRINT_FLOW(); int rc = VINF_SUCCESS; /* Make sure all pending writes are flushed */ rc = shaFlushCurBuf(pInt); if (pInt->pWorkerThread) { /* Signal the worker thread to end himself */ rc = shaSignalManifestThread(pInt, STATUS_END); /* Worker thread stopped? */ rc = RTThreadWait(pInt->pWorkerThread, RT_INDEFINITE_WAIT, 0); } if ( RT_SUCCESS(rc) && pShaStorage->fCreateDigest) { /* Finally calculate & format the SHA1/SHA256 sum */ unsigned char auchDig[RTSHA256_HASH_SIZE]; char *pszDigest; size_t cbDigest; if (pShaStorage->fSha256) { RTSha256Final(&pInt->ctx.Sha256, auchDig); cbDigest = RTSHA256_DIGEST_LEN; } else { RTSha1Final(&pInt->ctx.Sha1, auchDig); cbDigest = RTSHA1_DIGEST_LEN; } rc = RTStrAllocEx(&pszDigest, cbDigest + 1); if (RT_SUCCESS(rc)) { if (pShaStorage->fSha256) rc = RTSha256ToString(auchDig, pszDigest, cbDigest + 1); else rc = RTSha1ToString(auchDig, pszDigest, cbDigest + 1); if (RT_SUCCESS(rc)) pShaStorage->strDigest = pszDigest; RTStrFree(pszDigest); } } /* Close the file */ rc = vdIfIoFileClose(pIfIo, pInt->pvStorage); // RTPrintf("%lu %lu\n", pInt->calls, pInt->waits); /* Cleanup */ if (pInt->workFinishedEvent) RTSemEventDestroy(pInt->workFinishedEvent); if (pInt->newStatusEvent) RTSemEventDestroy(pInt->newStatusEvent); if (pInt->pCircBuf) RTCircBufDestroy(pInt->pCircBuf); if (pInt->pvZeroBuf) RTMemFree(pInt->pvZeroBuf); RTMemFree(pInt); return rc; } static int shaDeleteCallback(void *pvUser, const char *pcszFilename) { /* Validate input. */ AssertPtrReturn(pvUser, VERR_INVALID_POINTER); PSHASTORAGE pShaStorage = (PSHASTORAGE)pvUser; PVDINTERFACEIO pIfIo = VDIfIoGet(pShaStorage->pVDImageIfaces); AssertPtrReturn(pIfIo, VERR_INVALID_PARAMETER); DEBUG_PRINT_FLOW(); return vdIfIoFileDelete(pIfIo, pcszFilename); } static int shaMoveCallback(void *pvUser, const char *pcszSrc, const char *pcszDst, unsigned fMove) { /* Validate input. */ AssertPtrReturn(pvUser, VERR_INVALID_POINTER); PSHASTORAGE pShaStorage = (PSHASTORAGE)pvUser; PVDINTERFACEIO pIfIo = VDIfIoGet(pShaStorage->pVDImageIfaces); AssertPtrReturn(pIfIo, VERR_INVALID_PARAMETER); DEBUG_PRINT_FLOW(); return vdIfIoFileMove(pIfIo, pcszSrc, pcszDst, fMove); } static int shaGetFreeSpaceCallback(void *pvUser, const char *pcszFilename, int64_t *pcbFreeSpace) { /* Validate input. */ AssertPtrReturn(pvUser, VERR_INVALID_POINTER); PSHASTORAGE pShaStorage = (PSHASTORAGE)pvUser; PVDINTERFACEIO pIfIo = VDIfIoGet(pShaStorage->pVDImageIfaces); AssertPtrReturn(pIfIo, VERR_INVALID_PARAMETER); DEBUG_PRINT_FLOW(); return vdIfIoFileGetFreeSpace(pIfIo, pcszFilename, pcbFreeSpace); } static int shaGetModificationTimeCallback(void *pvUser, const char *pcszFilename, PRTTIMESPEC pModificationTime) { /* Validate input. */ AssertPtrReturn(pvUser, VERR_INVALID_POINTER); PSHASTORAGE pShaStorage = (PSHASTORAGE)pvUser; PVDINTERFACEIO pIfIo = VDIfIoGet(pShaStorage->pVDImageIfaces); AssertPtrReturn(pIfIo, VERR_INVALID_PARAMETER); DEBUG_PRINT_FLOW(); return vdIfIoFileGetModificationTime(pIfIo, pcszFilename, pModificationTime); } static int shaGetSizeCallback(void *pvUser, void *pvStorage, uint64_t *pcbSize) { /* Validate input. */ AssertPtrReturn(pvUser, VERR_INVALID_POINTER); AssertPtrReturn(pvStorage, VERR_INVALID_POINTER); PSHASTORAGE pShaStorage = (PSHASTORAGE)pvUser; PVDINTERFACEIO pIfIo = VDIfIoGet(pShaStorage->pVDImageIfaces); AssertPtrReturn(pIfIo, VERR_INVALID_PARAMETER); PSHASTORAGEINTERNAL pInt = (PSHASTORAGEINTERNAL)pvStorage; DEBUG_PRINT_FLOW(); uint64_t cbSize; int rc = vdIfIoFileGetSize(pIfIo, pInt->pvStorage, &cbSize); if (RT_FAILURE(rc)) return rc; *pcbSize = RT_MAX(pInt->cbCurAll, cbSize); return VINF_SUCCESS; } static int shaSetSizeCallback(void *pvUser, void *pvStorage, uint64_t cbSize) { /* Validate input. */ AssertPtrReturn(pvUser, VERR_INVALID_POINTER); AssertPtrReturn(pvStorage, VERR_INVALID_POINTER); PSHASTORAGE pShaStorage = (PSHASTORAGE)pvUser; PVDINTERFACEIO pIfIo = VDIfIoGet(pShaStorage->pVDImageIfaces); AssertPtrReturn(pIfIo, VERR_INVALID_PARAMETER); PSHASTORAGEINTERNAL pInt = (PSHASTORAGEINTERNAL)pvStorage; DEBUG_PRINT_FLOW(); return vdIfIoFileSetSize(pIfIo, pInt->pvStorage, cbSize); } static int shaWriteSyncCallback(void *pvUser, void *pvStorage, uint64_t uOffset, const void *pvBuf, size_t cbWrite, size_t *pcbWritten) { /* Validate input. */ AssertPtrReturn(pvUser, VERR_INVALID_POINTER); AssertPtrReturn(pvStorage, VERR_INVALID_POINTER); PSHASTORAGE pShaStorage = (PSHASTORAGE)pvUser; PVDINTERFACEIO pIfIo = VDIfIoGet(pShaStorage->pVDImageIfaces); AssertPtrReturn(pIfIo, VERR_INVALID_PARAMETER); PSHASTORAGEINTERNAL pInt = (PSHASTORAGEINTERNAL)pvStorage; DEBUG_PRINT_FLOW(); /* 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. */ if (pInt->cbCurAll < uOffset) { size_t cbSize = (size_t)(uOffset - pInt->cbCurAll); size_t cbAllWritten = 0; for (;;) { /* Finished? */ if (cbAllWritten == cbSize) break; size_t cbToWrite = RT_MIN(pInt->cbZeroBuf, cbSize - cbAllWritten); size_t cbWritten = 0; rc = shaWriteSyncCallback(pvUser, pvStorage, pInt->cbCurAll, pInt->pvZeroBuf, cbToWrite, &cbWritten); if (RT_FAILURE(rc)) break; cbAllWritten += cbWritten; } 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; size_t cbAvail = RTCircBufFree(pInt->pCircBuf); if ( cbAvail == 0 && pInt->fEOF) return VERR_EOF; /* If there isn't enough free space make sure the worker thread is * writing some data. */ if ((cbWrite - cbAllWritten) > cbAvail) { rc = shaSignalManifestThread(pInt, STATUS_WRITE); if (RT_FAILURE(rc)) break; /* If there is _no_ free space available, we have to wait until it is. */ if (cbAvail == 0) { rc = shaWaitForManifestThreadFinished(pInt); if (RT_FAILURE(rc)) break; cbAvail = RTCircBufFree(pInt->pCircBuf); // RTPrintf("############## wait %lu %lu %lu \n", cbRead, cbAllRead, cbAvail); // pInt->waits++; } } size_t cbToWrite = RT_MIN(cbWrite - cbAllWritten, cbAvail); char *pcBuf; size_t cbMemWritten = 0; /* Acquire a block for writing from our circular buffer. */ RTCircBufAcquireWriteBlock(pInt->pCircBuf, cbToWrite, (void**)&pcBuf, &cbMemWritten); memcpy(pcBuf, &((char*)pvBuf)[cbAllWritten], cbMemWritten); /* Mark the block full. */ RTCircBufReleaseWriteBlock(pInt->pCircBuf, cbMemWritten); cbAllWritten += cbMemWritten; pInt->cbCurAll += cbMemWritten; } if (pcbWritten) *pcbWritten = cbAllWritten; /* Signal the thread to write more data in the mean time. */ if ( RT_SUCCESS(rc) && RTCircBufUsed(pInt->pCircBuf) >= (RTCircBufSize(pInt->pCircBuf) / 2)) rc = shaSignalManifestThread(pInt, STATUS_WRITE); return rc; } static int shaReadSyncCallback(void *pvUser, void *pvStorage, uint64_t uOffset, void *pvBuf, size_t cbRead, size_t *pcbRead) { /* Validate input. */ AssertPtrReturn(pvUser, VERR_INVALID_POINTER); AssertPtrReturn(pvStorage, VERR_INVALID_POINTER); PSHASTORAGE pShaStorage = (PSHASTORAGE)pvUser; PVDINTERFACEIO pIfIo = VDIfIoGet(pShaStorage->pVDImageIfaces); AssertPtrReturn(pIfIo, VERR_INVALID_PARAMETER); // DEBUG_PRINT_FLOW(); PSHASTORAGEINTERNAL pInt = (PSHASTORAGEINTERNAL)pvStorage; 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/SHA256; streaming). */ if (pInt->cbCurAll < uOffset) { rc = shaReadSyncCallback(pvUser, pvStorage, pInt->cbCurAll, 0, (size_t)(uOffset - pInt->cbCurAll), 0); if (RT_FAILURE(rc)) return rc; // RTPrintf("Gap Read uOffset: %7lu cbRead: %7lu = %7lu\n", uOffset, cbRead, uOffset + cbRead); } else if (uOffset < pInt->cbCurAll) AssertMsgFailed(("Jumping backwards is not possible, sequential access is supported only\n")); size_t cbAllRead = 0; size_t cbAvail = 0; for (;;) { /* Finished? */ if (cbAllRead == cbRead) break; cbAvail = RTCircBufUsed(pInt->pCircBuf); if ( cbAvail == 0 && pInt->fEOF && !RTCircBufIsWriting(pInt->pCircBuf)) { rc = VINF_EOF; break; } /* If there isn't enough data make sure the worker thread is fetching * more. */ if ((cbRead - cbAllRead) > cbAvail) { rc = shaSignalManifestThread(pInt, STATUS_READ); if (RT_FAILURE(rc)) break; /* If there is _no_ data available, we have to wait until it is. */ if (cbAvail == 0) { rc = shaWaitForManifestThreadFinished(pInt); if (RT_FAILURE(rc)) break; cbAvail = RTCircBufUsed(pInt->pCircBuf); // RTPrintf("############## wait %lu %lu %lu \n", cbRead, cbAllRead, cbAvail); // pInt->waits++; } } size_t cbToRead = RT_MIN(cbRead - cbAllRead, cbAvail); char *pcBuf; size_t cbMemRead = 0; /* Acquire a block for reading from our circular buffer. */ RTCircBufAcquireReadBlock(pInt->pCircBuf, cbToRead, (void**)&pcBuf, &cbMemRead); if (pvBuf) /* Make it possible to blind read data (for skipping) */ memcpy(&((char*)pvBuf)[cbAllRead], pcBuf, cbMemRead); /* Mark the block as empty again. */ RTCircBufReleaseReadBlock(pInt->pCircBuf, cbMemRead); cbAllRead += cbMemRead; pInt->cbCurAll += cbMemRead; } if (pcbRead) *pcbRead = cbAllRead; if (rc == VERR_EOF) rc = VINF_SUCCESS; /* Signal the thread to read more data in the mean time. */ if ( RT_SUCCESS(rc) && rc != VINF_EOF && RTCircBufFree(pInt->pCircBuf) >= (RTCircBufSize(pInt->pCircBuf) / 2)) rc = shaSignalManifestThread(pInt, STATUS_READ); return rc; } static int shaFlushSyncCallback(void *pvUser, void *pvStorage) { /* Validate input. */ AssertPtrReturn(pvUser, VERR_INVALID_POINTER); AssertPtrReturn(pvStorage, VERR_INVALID_POINTER); PSHASTORAGE pShaStorage = (PSHASTORAGE)pvUser; PVDINTERFACEIO pIfIo = VDIfIoGet(pShaStorage->pVDImageIfaces); AssertPtrReturn(pIfIo, VERR_INVALID_PARAMETER); DEBUG_PRINT_FLOW(); PSHASTORAGEINTERNAL pInt = (PSHASTORAGEINTERNAL)pvStorage; /* Check if there is still something in the buffer. If yes, flush it. */ int rc = shaFlushCurBuf(pInt); if (RT_FAILURE(rc)) return rc; return vdIfIoFileFlushSync(pIfIo, pInt->pvStorage); } PVDINTERFACEIO ShaCreateInterface() { PVDINTERFACEIO pCallbacks = (PVDINTERFACEIO)RTMemAllocZ(sizeof(VDINTERFACEIO)); if (!pCallbacks) return NULL; pCallbacks->pfnOpen = shaOpenCallback; pCallbacks->pfnClose = shaCloseCallback; pCallbacks->pfnDelete = shaDeleteCallback; pCallbacks->pfnMove = shaMoveCallback; pCallbacks->pfnGetFreeSpace = shaGetFreeSpaceCallback; pCallbacks->pfnGetModificationTime = shaGetModificationTimeCallback; pCallbacks->pfnGetSize = shaGetSizeCallback; pCallbacks->pfnSetSize = shaSetSizeCallback; pCallbacks->pfnReadSync = shaReadSyncCallback; pCallbacks->pfnWriteSync = shaWriteSyncCallback; pCallbacks->pfnFlushSync = shaFlushSyncCallback; return pCallbacks; } PVDINTERFACEIO FileCreateInterface() { PVDINTERFACEIO pCallbacks = (PVDINTERFACEIO)RTMemAllocZ(sizeof(VDINTERFACEIO)); if (!pCallbacks) return NULL; pCallbacks->pfnOpen = fileOpenCallback; pCallbacks->pfnClose = fileCloseCallback; pCallbacks->pfnDelete = fileDeleteCallback; pCallbacks->pfnMove = fileMoveCallback; pCallbacks->pfnGetFreeSpace = fileGetFreeSpaceCallback; pCallbacks->pfnGetModificationTime = fileGetModificationTimeCallback; pCallbacks->pfnGetSize = fileGetSizeCallback; pCallbacks->pfnSetSize = fileSetSizeCallback; pCallbacks->pfnReadSync = fileReadSyncCallback; pCallbacks->pfnWriteSync = fileWriteSyncCallback; pCallbacks->pfnFlushSync = fileFlushSyncCallback; return pCallbacks; } int readFileIntoBuffer(const char *pcszFilename, void **ppvBuf, size_t *pcbSize, PVDINTERFACEIO pIfIo, void *pvUser) { /* Validate input. */ AssertPtrReturn(ppvBuf, VERR_INVALID_POINTER); AssertPtrReturn(pcbSize, VERR_INVALID_POINTER); AssertPtrReturn(pIfIo, VERR_INVALID_POINTER); void *pvStorage; int rc = pIfIo->pfnOpen(pvUser, pcszFilename, RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, 0, &pvStorage); if (RT_FAILURE(rc)) return rc; void *pvTmpBuf = 0; void *pvBuf = 0; uint64_t cbTmpSize = _1M; size_t cbAllRead = 0; do { pvTmpBuf = RTMemAlloc(cbTmpSize); if (!pvTmpBuf) { rc = VERR_NO_MEMORY; break; } for (;;) { size_t cbRead = 0; rc = pIfIo->pfnReadSync(pvUser, pvStorage, cbAllRead, pvTmpBuf, cbTmpSize, &cbRead); if ( RT_FAILURE(rc) || cbRead == 0) break; pvBuf = RTMemRealloc(pvBuf, cbAllRead + cbRead); if (!pvBuf) { rc = VERR_NO_MEMORY; break; } memcpy(&((char*)pvBuf)[cbAllRead], pvTmpBuf, cbRead); cbAllRead += cbRead; } } while (0); pIfIo->pfnClose(pvUser, pvStorage); if (rc == VERR_EOF) rc = VINF_SUCCESS; if (pvTmpBuf) RTMemFree(pvTmpBuf); if (RT_SUCCESS(rc)) { *ppvBuf = pvBuf; *pcbSize = cbAllRead; } else { if (pvBuf) RTMemFree(pvBuf); } return rc; } int writeBufferToFile(const char *pcszFilename, void *pvBuf, size_t cbSize, PVDINTERFACEIO pIfIo, void *pvUser) { /* Validate input. */ AssertPtrReturn(pvBuf, VERR_INVALID_POINTER); AssertReturn(cbSize, VERR_INVALID_PARAMETER); AssertPtrReturn(pIfIo, VERR_INVALID_POINTER); void *pvStorage; int rc = pIfIo->pfnOpen(pvUser, pcszFilename, RTFILE_O_CREATE | RTFILE_O_WRITE | RTFILE_O_DENY_ALL, 0, &pvStorage); if (RT_FAILURE(rc)) return rc; size_t cbAllWritten = 0; for (;;) { if (cbAllWritten >= cbSize) break; size_t cbToWrite = cbSize - cbAllWritten; size_t cbWritten = 0; rc = pIfIo->pfnWriteSync(pvUser, pvStorage, cbAllWritten, &((char*)pvBuf)[cbAllWritten], cbToWrite, &cbWritten); if (RT_FAILURE(rc)) break; cbAllWritten += cbWritten; } rc = pIfIo->pfnClose(pvUser, pvStorage); return rc; } int decompressImageAndSave(const char *pcszFullFilenameIn, const char *pcszFullFilenameOut, PVDINTERFACEIO pIfIo, void *pvUser) { /* Validate input. */ AssertPtrReturn(pIfIo, VERR_INVALID_POINTER); PSHASTORAGE pShaStorage = (PSHASTORAGE)pvUser; /* * Open the source file. */ void *pvStorage; int rc = pIfIo->pfnOpen(pvUser, pcszFullFilenameIn, RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, 0, &pvStorage); if (RT_FAILURE(rc)) return rc; /* Turn the source file handle/whatever into a VFS stream. */ RTVFSIOSTREAM hVfsIosCompressedSrc; rc = VDIfCreateVfsStream(pIfIo, pvStorage, RTFILE_O_READ, &hVfsIosCompressedSrc); if (RT_SUCCESS(rc)) { /* Pass the source thru gunzip. */ RTVFSIOSTREAM hVfsIosSrc; rc = RTZipGzipDecompressIoStream(hVfsIosCompressedSrc, 0, &hVfsIosSrc); if (RT_SUCCESS(rc)) { /* * Create the output file, including necessary paths. * Any existing file will be overwritten. */ rc = VirtualBox::i_ensureFilePathExists(Utf8Str(pcszFullFilenameOut), true /*fCreate*/); if (RT_SUCCESS(rc)) { RTVFSIOSTREAM hVfsIosDst; rc = RTVfsIoStrmOpenNormal(pcszFullFilenameOut, RTFILE_O_CREATE_REPLACE | RTFILE_O_WRITE | RTFILE_O_DENY_ALL, &hVfsIosDst); if (RT_SUCCESS(rc)) { /* * Pump the bytes thru. If we fail, delete the output file. */ RTMANIFEST hFileManifest = NIL_RTMANIFEST; rc = RTManifestCreate(0 /*fFlags*/, &hFileManifest); if (RT_SUCCESS(rc)) { RTVFSIOSTREAM hVfsIosMfst; uint32_t digestType = (pShaStorage->fSha256 == true) ? RTMANIFEST_ATTR_SHA256: RTMANIFEST_ATTR_SHA1; rc = RTManifestEntryAddPassthruIoStream(hFileManifest, hVfsIosSrc, "ovf import", digestType, true /*read*/, &hVfsIosMfst); if (RT_SUCCESS(rc)) { rc = RTVfsUtilPumpIoStreams(hVfsIosMfst, hVfsIosDst, 0); } RTVfsIoStrmRelease(hVfsIosMfst); } RTVfsIoStrmRelease(hVfsIosDst); } } RTVfsIoStrmRelease(hVfsIosSrc); } } pIfIo->pfnClose(pvUser, pvStorage); return rc; } int copyFileAndCalcShaDigest(const char *pcszSourceFilename, const char *pcszTargetFilename, PVDINTERFACEIO pIfIo, void *pvUser) { /* Validate input. */ AssertPtrReturn(pIfIo, VERR_INVALID_POINTER); PSHASTORAGE pShaStorage = (PSHASTORAGE)pvUser; void *pvStorage; int rc = pIfIo->pfnOpen(pvUser, pcszSourceFilename, RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, 0, &pvStorage); if (RT_FAILURE(rc)) return rc; /* Turn the source file handle/whatever into a VFS stream. */ RTVFSIOSTREAM hVfsIosSrc; rc = VDIfCreateVfsStream(pIfIo, pvStorage, RTFILE_O_READ, &hVfsIosSrc); if (RT_SUCCESS(rc)) { /* * Create the output file, including necessary paths. * Any existing file will be overwritten. */ rc = VirtualBox::i_ensureFilePathExists(Utf8Str(pcszTargetFilename), true /*fCreate*/); if (RT_SUCCESS(rc)) { RTVFSIOSTREAM hVfsIosDst; rc = RTVfsIoStrmOpenNormal(pcszTargetFilename, RTFILE_O_CREATE_REPLACE | RTFILE_O_WRITE | RTFILE_O_DENY_ALL, &hVfsIosDst); if (RT_SUCCESS(rc)) { /* * Pump the bytes thru. If we fail, delete the output file. */ RTMANIFEST hFileManifest = NIL_RTMANIFEST; rc = RTManifestCreate(0 /*fFlags*/, &hFileManifest); if (RT_SUCCESS(rc)) { RTVFSIOSTREAM hVfsIosMfst; uint32_t digestType = (pShaStorage->fSha256 == true) ? RTMANIFEST_ATTR_SHA256: RTMANIFEST_ATTR_SHA1; rc = RTManifestEntryAddPassthruIoStream(hFileManifest, hVfsIosSrc, "ovf import", digestType, true /*read*/, &hVfsIosMfst); if (RT_SUCCESS(rc)) { rc = RTVfsUtilPumpIoStreams(hVfsIosMfst, hVfsIosDst, 0); } RTVfsIoStrmRelease(hVfsIosMfst); } RTVfsIoStrmRelease(hVfsIosDst); } } RTVfsIoStrmRelease(hVfsIosSrc); } pIfIo->pfnClose(pvUser, pvStorage); return rc; }