diff options
Diffstat (limited to 'src/VBox/Runtime/common/vfs/vfsmemory.cpp')
-rw-r--r-- | src/VBox/Runtime/common/vfs/vfsmemory.cpp | 943 |
1 files changed, 943 insertions, 0 deletions
diff --git a/src/VBox/Runtime/common/vfs/vfsmemory.cpp b/src/VBox/Runtime/common/vfs/vfsmemory.cpp new file mode 100644 index 00000000..d29da969 --- /dev/null +++ b/src/VBox/Runtime/common/vfs/vfsmemory.cpp @@ -0,0 +1,943 @@ +/* $Id: vfsmemory.cpp $ */ +/** @file + * IPRT - Virtual File System, Memory Backed VFS. + */ + +/* + * Copyright (C) 2010-2019 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. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include "internal/iprt.h" +#include <iprt/vfs.h> + +#include <iprt/asm.h> +#include <iprt/assert.h> +#include <iprt/err.h> +#include <iprt/file.h> +#include <iprt/list.h> +#include <iprt/poll.h> +#include <iprt/string.h> +#include <iprt/vfslowlevel.h> + + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include "internal/iprt.h" +#include <iprt/vfs.h> + +#include <iprt/err.h> +#include <iprt/mem.h> + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +/** The max extent size. */ +#define RTVFSMEM_MAX_EXTENT_SIZE _2M + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ + +/** + * Memory base object info. + */ +typedef struct RTVFSMEMBASE +{ + /** The basic object info. */ + RTFSOBJINFO ObjInfo; +} RTVFSMEMBASE; + + +/** + * Memory file extent. + * + * This stores part of the file content. + */ +typedef struct RTVFSMEMEXTENT +{ + /** Extent list entry. */ + RTLISTNODE Entry; + /** The offset of this extent within the file. */ + uint64_t off; + /** The size of the this extent. */ + uint32_t cb; + /** The data. */ + uint8_t abData[1]; +} RTVFSMEMEXTENT; +/** Pointer to a memory file extent. */ +typedef RTVFSMEMEXTENT *PRTVFSMEMEXTENT; + +/** + * Memory file. + */ +typedef struct RTVFSMEMFILE +{ + /** The base info. */ + RTVFSMEMBASE Base; + /** The current file position. */ + uint64_t offCurPos; + /** Pointer to the current file extent. */ + PRTVFSMEMEXTENT pCurExt; + /** Linked list of file extents - RTVFSMEMEXTENT. */ + RTLISTANCHOR ExtentHead; + /** The current extent size. + * This is slowly grown to RTVFSMEM_MAX_EXTENT_SIZE as the file grows. */ + uint32_t cbExtent; +} RTVFSMEMFILE; +/** Pointer to a memory file. */ +typedef RTVFSMEMFILE *PRTVFSMEMFILE; + + + +/** + * @interface_method_impl{RTVFSOBJOPS,pfnClose} + */ +static DECLCALLBACK(int) rtVfsMemFile_Close(void *pvThis) +{ + PRTVFSMEMFILE pThis = (PRTVFSMEMFILE)pvThis; + + /* + * Free the extent list. + */ + PRTVFSMEMEXTENT pCur, pNext; + RTListForEachSafe(&pThis->ExtentHead, pCur, pNext, RTVFSMEMEXTENT, Entry) + { + pCur->off = RTFOFF_MAX; + pCur->cb = UINT32_MAX; + RTListNodeRemove(&pCur->Entry); + RTMemFree(pCur); + } + pThis->pCurExt = NULL; + + return VINF_SUCCESS; +} + + +/** + * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo} + */ +static DECLCALLBACK(int) rtVfsMemFile_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr) +{ + PRTVFSMEMFILE pThis = (PRTVFSMEMFILE)pvThis; + switch (enmAddAttr) + { + case RTFSOBJATTRADD_NOTHING: + case RTFSOBJATTRADD_UNIX: + *pObjInfo = pThis->Base.ObjInfo; + return VINF_SUCCESS; + + default: + return VERR_NOT_SUPPORTED; + } +} + + +/** + * The slow paths of rtVfsMemFile_LocateExtent. + * + * @copydoc rtVfsMemFile_LocateExtent + */ +static PRTVFSMEMEXTENT rtVfsMemFile_LocateExtentSlow(PRTVFSMEMFILE pThis, uint64_t off, bool *pfHit) +{ + /* + * Search from the start or the previously used extent. The heuristics + * are very very simple, but whatever. + */ + PRTVFSMEMEXTENT pExtent = pThis->pCurExt; + if (!pExtent || off < pExtent->off) + { + /* Consider the last entry first (for writes). */ + pExtent = RTListGetLast(&pThis->ExtentHead, RTVFSMEMEXTENT, Entry); + if (!pExtent) + { + *pfHit = false; + return NULL; + } + if (off - pExtent->off < pExtent->cb) + { + *pfHit = true; + pThis->pCurExt = pExtent; + return pExtent; + } + + /* Otherwise, start from the head. */ + pExtent = RTListGetFirst(&pThis->ExtentHead, RTVFSMEMEXTENT, Entry); + } + + while (off - pExtent->off >= pExtent->cb) + { + Assert(pExtent->off <= off); + PRTVFSMEMEXTENT pNext = RTListGetNext(&pThis->ExtentHead, pExtent, RTVFSMEMEXTENT, Entry); + if ( !pNext + || pNext->off > off) + { + *pfHit = false; + return pNext; + } + + pExtent = pNext; + } + + *pfHit = true; + pThis->pCurExt = pExtent; + return pExtent; +} + + +/** + * Locates the extent covering the specified offset, or the one after it. + * + * @returns The closest extent. NULL if off is 0 and there are no extent + * covering byte 0 yet. + * @param pThis The memory file. + * @param off The offset (0-positive). + * @param pfHit Where to indicate whether the extent is a + * direct hit (@c true) or just a closest match + * (@c false). + */ +DECLINLINE(PRTVFSMEMEXTENT) rtVfsMemFile_LocateExtent(PRTVFSMEMFILE pThis, uint64_t off, bool *pfHit) +{ + /* + * The most likely case is that we're hitting the extent we used in the + * previous access or the one immediately following it. + */ + PRTVFSMEMEXTENT pExtent = pThis->pCurExt; + if (!pExtent) + return rtVfsMemFile_LocateExtentSlow(pThis, off, pfHit); + + if (off - pExtent->off >= pExtent->cb) + { + pExtent = RTListGetNext(&pThis->ExtentHead, pExtent, RTVFSMEMEXTENT, Entry); + if ( !pExtent + || off - pExtent->off >= pExtent->cb) + return rtVfsMemFile_LocateExtentSlow(pThis, off, pfHit); + pThis->pCurExt = pExtent; + } + + *pfHit = true; + return pExtent; +} + + +/** + * @interface_method_impl{RTVFSIOSTREAMOPS,pfnRead} + */ +static DECLCALLBACK(int) rtVfsMemFile_Read(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbRead) +{ + PRTVFSMEMFILE pThis = (PRTVFSMEMFILE)pvThis; + + Assert(pSgBuf->cSegs == 1); + NOREF(fBlocking); + + /* + * Find the current position and check if it's within the file. + */ + uint64_t offUnsigned = off < 0 ? pThis->offCurPos : (uint64_t)off; + if (offUnsigned >= (uint64_t)pThis->Base.ObjInfo.cbObject) + { + if (pcbRead) + { + *pcbRead = 0; + pThis->offCurPos = offUnsigned; + return VINF_EOF; + } + return VERR_EOF; + } + + size_t cbLeftToRead; + if (offUnsigned + pSgBuf->paSegs[0].cbSeg > (uint64_t)pThis->Base.ObjInfo.cbObject) + { + if (!pcbRead) + return VERR_EOF; + *pcbRead = cbLeftToRead = (size_t)((uint64_t)pThis->Base.ObjInfo.cbObject - offUnsigned); + } + else + { + cbLeftToRead = pSgBuf->paSegs[0].cbSeg; + if (pcbRead) + *pcbRead = cbLeftToRead; + } + + /* + * Ok, we've got a valid stretch within the file. Do the reading. + */ + if (cbLeftToRead > 0) + { + uint8_t *pbDst = (uint8_t *)pSgBuf->paSegs[0].pvSeg; + bool fHit; + PRTVFSMEMEXTENT pExtent = rtVfsMemFile_LocateExtent(pThis, offUnsigned, &fHit); + for (;;) + { + size_t cbThisRead; + + /* + * Do we hit an extent covering the current file surface? + */ + if (fHit) + { + /* Yes, copy the data. */ + Assert(offUnsigned - pExtent->off < pExtent->cb); + size_t const offExtent = (size_t)(offUnsigned - pExtent->off); + cbThisRead = pExtent->cb - offExtent; + if (cbThisRead >= cbLeftToRead) + cbThisRead = cbLeftToRead; + + memcpy(pbDst, &pExtent->abData[offUnsigned - pExtent->off], cbThisRead); + + offUnsigned += cbThisRead; + cbLeftToRead -= cbThisRead; + if (!cbLeftToRead) + break; + pbDst += cbThisRead; + + /* Advance, looping immediately if not sparse. */ + PRTVFSMEMEXTENT pNext = RTListGetNext(&pThis->ExtentHead, pExtent, RTVFSMEMEXTENT, Entry); + if ( pNext + && pNext->off == pExtent->off + pExtent->cb) + { + pExtent = pNext; + continue; + } + + Assert(!pNext || pNext->off > pExtent->off); + pExtent = pNext; + fHit = false; + } + else + Assert(!pExtent || pExtent->off > offUnsigned); + + /* + * No extent of this portion (sparse file) - Read zeros. + */ + if ( !pExtent + || offUnsigned + cbLeftToRead <= pExtent->off) + cbThisRead = cbLeftToRead; + else + cbThisRead = (size_t)(pExtent->off - offUnsigned); + + RT_BZERO(pbDst, cbThisRead); + + offUnsigned += cbThisRead; + cbLeftToRead -= cbThisRead; + if (!cbLeftToRead) + break; + pbDst += cbThisRead; + + /* Go on and read content from the next extent. */ + fHit = true; + } + } + + pThis->offCurPos = offUnsigned; + return VINF_SUCCESS; +} + + +/** + * Allocates a new extent covering the ground at @a offUnsigned. + * + * @returns Pointer to the new extent on success, NULL if we're out of memory. + * @param pThis The memory file. + * @param offUnsigned The location to allocate the extent at. + * @param cbToWrite The number of bytes we're interested in writing + * starting at @a offUnsigned. + * @param pNext The extention after @a offUnsigned. NULL if + * none, i.e. we're allocating space at the end of + * the file. + */ +static PRTVFSMEMEXTENT rtVfsMemFile_AllocExtent(PRTVFSMEMFILE pThis, uint64_t offUnsigned, size_t cbToWrite, + PRTVFSMEMEXTENT pNext) +{ + /* + * Adjust the extent size if we haven't reached the max size yet. + */ + if (pThis->cbExtent != RTVFSMEM_MAX_EXTENT_SIZE) + { + if (cbToWrite >= RTVFSMEM_MAX_EXTENT_SIZE) + pThis->cbExtent = RTVFSMEM_MAX_EXTENT_SIZE; + else if (!RTListIsEmpty(&pThis->ExtentHead)) + { + uint32_t cbNextExtent = pThis->cbExtent; + if (RT_IS_POWER_OF_TWO(cbNextExtent)) + cbNextExtent *= 2; + else + { + /* Make it a power of two (seeRTVfsMemorizeIoStreamAsFile). */ + cbNextExtent = _4K; + while (cbNextExtent < pThis->cbExtent) + cbNextExtent *= 2; + } + if (((pThis->Base.ObjInfo.cbAllocated + cbNextExtent) & (cbNextExtent - 1)) == 0) + pThis->cbExtent = cbNextExtent; + } + } + + /* + * Figure out the size and position of the extent we're adding. + */ + uint64_t offExtent = offUnsigned & ~(uint64_t)(pThis->cbExtent - 1); + uint32_t cbExtent = pThis->cbExtent; + + PRTVFSMEMEXTENT pPrev = pNext + ? RTListGetPrev(&pThis->ExtentHead, pNext, RTVFSMEMEXTENT, Entry) + : RTListGetLast(&pThis->ExtentHead, RTVFSMEMEXTENT, Entry); + uint64_t const offPrev = pPrev ? pPrev->off + pPrev->cb : 0; + if (offExtent < offPrev) + offExtent = offPrev; + + if (pNext) + { + uint64_t cbMaxExtent = pNext->off - offExtent; + if (cbMaxExtent < cbExtent) + cbExtent = (uint32_t)cbMaxExtent; + } + + /* + * Allocate, initialize and insert the new extent. + */ + PRTVFSMEMEXTENT pNew = (PRTVFSMEMEXTENT)RTMemAllocZ(RT_UOFFSETOF_DYN(RTVFSMEMEXTENT, abData[cbExtent])); + if (pNew) + { + pNew->off = offExtent; + pNew->cb = cbExtent; + if (pPrev) + RTListNodeInsertAfter(&pPrev->Entry, &pNew->Entry); + else + RTListPrepend(&pThis->ExtentHead, &pNew->Entry); + + pThis->Base.ObjInfo.cbAllocated += cbExtent; + } + /** @todo retry with minimum size. */ + + return pNew; +} + + +/** + * @interface_method_impl{RTVFSIOSTREAMOPS,pfnWrite} + */ +static DECLCALLBACK(int) rtVfsMemFile_Write(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbWritten) +{ + PRTVFSMEMFILE pThis = (PRTVFSMEMFILE)pvThis; + + Assert(pSgBuf->cSegs == 1); + NOREF(fBlocking); + + /* + * Validate the write and set up the write loop. + */ + size_t cbLeftToWrite = pSgBuf->paSegs[0].cbSeg; + if (!cbLeftToWrite) + return VINF_SUCCESS; /* pcbWritten is already 0. */ + uint64_t offUnsigned = off < 0 ? pThis->offCurPos : (uint64_t)off; + if (offUnsigned + cbLeftToWrite >= (uint64_t)RTFOFF_MAX) + return VERR_OUT_OF_RANGE; + + int rc = VINF_SUCCESS; + uint8_t const *pbSrc = (uint8_t const *)pSgBuf->paSegs[0].pvSeg; + bool fHit; + PRTVFSMEMEXTENT pExtent = rtVfsMemFile_LocateExtent(pThis, offUnsigned, &fHit); + for (;;) + { + /* + * If we didn't hit an extent, allocate one (unless it's all zeros). + */ + if (!fHit) + { + Assert(!pExtent || pExtent->off > offUnsigned); + + /* Skip leading zeros if there is a whole bunch of them. */ + uint8_t const *pbSrcNZ = (uint8_t const *)ASMMemFirstNonZero(pbSrc, cbLeftToWrite); + size_t cbZeros = pbSrcNZ ? pbSrcNZ - pbSrc : cbLeftToWrite; + if (cbZeros) + { + uint64_t const cbToNext = pExtent ? pExtent->off - offUnsigned : UINT64_MAX; + if (cbZeros > cbToNext) + cbZeros = (size_t)cbToNext; + offUnsigned += cbZeros; + cbLeftToWrite -= cbZeros; + if (!cbLeftToWrite) + break; + + Assert(!pExtent || offUnsigned <= pExtent->off); + if (pExtent && pExtent->off == offUnsigned) + { + fHit = true; + continue; + } + } + + fHit = true; + pExtent = rtVfsMemFile_AllocExtent(pThis, offUnsigned, cbLeftToWrite, pExtent); + if (!pExtent) + { + rc = VERR_NO_MEMORY; + break; + } + } + Assert(offUnsigned - pExtent->off < pExtent->cb); + + /* + * Copy the source data into the current extent. + */ + uint32_t const offDst = (uint32_t)(offUnsigned - pExtent->off); + uint32_t cbThisWrite = pExtent->cb - offDst; + if (cbThisWrite > cbLeftToWrite) + cbThisWrite = (uint32_t)cbLeftToWrite; + memcpy(&pExtent->abData[offDst], pbSrc, cbThisWrite); + + offUnsigned += cbThisWrite; + cbLeftToWrite -= cbThisWrite; + if (!cbLeftToWrite) + break; + pbSrc += cbThisWrite; + Assert(offUnsigned == pExtent->off + pExtent->cb); + + /* + * Advance to the next extent (emulate the lookup). + */ + pExtent = RTListGetNext(&pThis->ExtentHead, pExtent, RTVFSMEMEXTENT, Entry); + fHit = pExtent && (offUnsigned - pExtent->off < pExtent->cb); + } + + /* + * Update the state, set return value and return. + * Note! There must be no alternative exit path from the loop above. + */ + pThis->offCurPos = offUnsigned; + if ((uint64_t)pThis->Base.ObjInfo.cbObject < offUnsigned) + pThis->Base.ObjInfo.cbObject = offUnsigned; + + if (pcbWritten) + *pcbWritten = pSgBuf->paSegs[0].cbSeg - cbLeftToWrite; + return rc; +} + + +/** + * @interface_method_impl{RTVFSIOSTREAMOPS,pfnFlush} + */ +static DECLCALLBACK(int) rtVfsMemFile_Flush(void *pvThis) +{ + NOREF(pvThis); + return VINF_SUCCESS; +} + + +/** + * @interface_method_impl{RTVFSIOSTREAMOPS,pfnTell} + */ +static DECLCALLBACK(int) rtVfsMemFile_Tell(void *pvThis, PRTFOFF poffActual) +{ + PRTVFSMEMFILE pThis = (PRTVFSMEMFILE)pvThis; + *poffActual = pThis->offCurPos; + return VINF_SUCCESS; +} + + +/** + * @interface_method_impl{RTVFSOBJSETOPS,pfnMode} + */ +static DECLCALLBACK(int) rtVfsMemFile_SetMode(void *pvThis, RTFMODE fMode, RTFMODE fMask) +{ + PRTVFSMEMFILE pThis = (PRTVFSMEMFILE)pvThis; + pThis->Base.ObjInfo.Attr.fMode = (pThis->Base.ObjInfo.Attr.fMode & ~fMask) | fMode; + return VINF_SUCCESS; +} + + +/** + * @interface_method_impl{RTVFSOBJSETOPS,pfnSetTimes} + */ +static DECLCALLBACK(int) rtVfsMemFile_SetTimes(void *pvThis, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime, + PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime) +{ + PRTVFSMEMFILE pThis = (PRTVFSMEMFILE)pvThis; + + if (pAccessTime) + pThis->Base.ObjInfo.AccessTime = *pAccessTime; + if (pModificationTime) + pThis->Base.ObjInfo.ModificationTime = *pModificationTime; + if (pChangeTime) + pThis->Base.ObjInfo.ChangeTime = *pChangeTime; + if (pBirthTime) + pThis->Base.ObjInfo.BirthTime = *pBirthTime; + + return VINF_SUCCESS; +} + + +/** + * @interface_method_impl{RTVFSOBJSETOPS,pfnSetOwner} + */ +static DECLCALLBACK(int) rtVfsMemFile_SetOwner(void *pvThis, RTUID uid, RTGID gid) +{ + PRTVFSMEMFILE pThis = (PRTVFSMEMFILE)pvThis; + + if (uid != NIL_RTUID) + pThis->Base.ObjInfo.Attr.u.Unix.uid = uid; + if (gid != NIL_RTUID) + pThis->Base.ObjInfo.Attr.u.Unix.gid = gid; + + return VINF_SUCCESS; +} + + +/** + * @interface_method_impl{RTVFSFILEOPS,pfnSeek} + */ +static DECLCALLBACK(int) rtVfsMemFile_Seek(void *pvThis, RTFOFF offSeek, unsigned uMethod, PRTFOFF poffActual) +{ + PRTVFSMEMFILE pThis = (PRTVFSMEMFILE)pvThis; + + /* + * Seek relative to which position. + */ + uint64_t offWrt; + switch (uMethod) + { + case RTFILE_SEEK_BEGIN: + offWrt = 0; + break; + + case RTFILE_SEEK_CURRENT: + offWrt = pThis->offCurPos; + break; + + case RTFILE_SEEK_END: + offWrt = pThis->Base.ObjInfo.cbObject; + break; + + default: + return VERR_INTERNAL_ERROR_5; + } + + /* + * Calc new position, take care to stay within RTFOFF type bounds. + */ + uint64_t offNew; + if (offSeek == 0) + offNew = offWrt; + else if (offSeek > 0) + { + offNew = offWrt + offSeek; + if ( offNew < offWrt + || offNew > RTFOFF_MAX) + offNew = RTFOFF_MAX; + } + else if ((uint64_t)-offSeek < offWrt) + offNew = offWrt + offSeek; + else + offNew = 0; + + /* + * Update the state and set return value. + */ + if ( pThis->pCurExt + && pThis->pCurExt->off - offNew >= pThis->pCurExt->cb) + pThis->pCurExt = NULL; + pThis->offCurPos = offNew; + + *poffActual = offNew; + return VINF_SUCCESS; +} + + +/** + * @interface_method_impl{RTVFSFILEOPS,pfnQuerySize} + */ +static DECLCALLBACK(int) rtVfsMemFile_QuerySize(void *pvThis, uint64_t *pcbFile) +{ + PRTVFSMEMFILE pThis = (PRTVFSMEMFILE)pvThis; + *pcbFile = pThis->Base.ObjInfo.cbObject; + return VINF_SUCCESS; +} + + +/** + * @interface_method_impl{RTVFSFILEOPS,pfnSetSize} + */ +static DECLCALLBACK(int) rtVfsMemFile_SetSize(void *pvThis, uint64_t cbFile, uint32_t fFlags) +{ + NOREF(pvThis); NOREF(cbFile); NOREF(fFlags); + AssertMsgFailed(("Lucky you! You get to implement this (or bug bird about it).\n")); + return VERR_NOT_IMPLEMENTED; +} + + +/** + * @interface_method_impl{RTVFSFILEOPS,pfnQueryMaxSize} + */ +static DECLCALLBACK(int) rtVfsMemFile_QueryMaxSize(void *pvThis, uint64_t *pcbMax) +{ + RT_NOREF(pvThis); + *pcbMax = ~(size_t)0 >> 1; + return VINF_SUCCESS; +} + + +/** + * Memory file operations. + */ +DECL_HIDDEN_CONST(const RTVFSFILEOPS) g_rtVfsMemFileOps = +{ + { /* Stream */ + { /* Obj */ + RTVFSOBJOPS_VERSION, + RTVFSOBJTYPE_FILE, + "MemFile", + rtVfsMemFile_Close, + rtVfsMemFile_QueryInfo, + RTVFSOBJOPS_VERSION + }, + RTVFSIOSTREAMOPS_VERSION, + RTVFSIOSTREAMOPS_FEAT_NO_SG, + rtVfsMemFile_Read, + rtVfsMemFile_Write, + rtVfsMemFile_Flush, + NULL /*PollOne*/, + rtVfsMemFile_Tell, + NULL /*Skip*/, + NULL /*ZeroFill*/, + RTVFSIOSTREAMOPS_VERSION, + }, + RTVFSFILEOPS_VERSION, + /*RTVFSIOFILEOPS_FEAT_NO_AT_OFFSET*/ 0, + { /* ObjSet */ + RTVFSOBJSETOPS_VERSION, + RT_UOFFSETOF(RTVFSFILEOPS, ObjSet) - RT_UOFFSETOF(RTVFSFILEOPS, Stream.Obj), + rtVfsMemFile_SetMode, + rtVfsMemFile_SetTimes, + rtVfsMemFile_SetOwner, + RTVFSOBJSETOPS_VERSION + }, + rtVfsMemFile_Seek, + rtVfsMemFile_QuerySize, + rtVfsMemFile_SetSize, + rtVfsMemFile_QueryMaxSize, + RTVFSFILEOPS_VERSION +}; + + +/** + * Initialize the RTVFSMEMFILE::Base.ObjInfo specific members. + * + * @param pObjInfo The object info to init. + * @param cbObject The object size set. + */ +static void rtVfsMemInitObjInfo(PRTFSOBJINFO pObjInfo, uint64_t cbObject) +{ + pObjInfo->cbObject = cbObject; + pObjInfo->cbAllocated = cbObject; + pObjInfo->Attr.fMode = RTFS_DOS_NT_NORMAL | RTFS_TYPE_FILE | RTFS_UNIX_IRWXU; + pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX; + pObjInfo->Attr.u.Unix.uid = NIL_RTUID; + pObjInfo->Attr.u.Unix.gid = NIL_RTGID; + pObjInfo->Attr.u.Unix.cHardlinks = 1; + pObjInfo->Attr.u.Unix.INodeIdDevice = 0; + pObjInfo->Attr.u.Unix.INodeId = 0; + pObjInfo->Attr.u.Unix.fFlags = 0; + pObjInfo->Attr.u.Unix.GenerationId = 0; + pObjInfo->Attr.u.Unix.Device = 0; + RTTimeNow(&pObjInfo->AccessTime); + pObjInfo->ModificationTime = pObjInfo->AccessTime; + pObjInfo->ChangeTime = pObjInfo->AccessTime; + pObjInfo->BirthTime = pObjInfo->AccessTime; +} + + +/** + * Initialize the RTVFSMEMFILE specific members. + * + * @param pThis The memory file to initialize. + * @param cbObject The object size for estimating extent size. + * @param fFlags The user specified flags. + */ +static void rtVfsMemFileInit(PRTVFSMEMFILE pThis, RTFOFF cbObject, uint32_t fFlags) +{ + pThis->offCurPos = 0; + pThis->pCurExt = NULL; + RTListInit(&pThis->ExtentHead); + if (cbObject <= 0) + pThis->cbExtent = _4K; + else if (cbObject < RTVFSMEM_MAX_EXTENT_SIZE) + pThis->cbExtent = fFlags & RTFILE_O_WRITE ? _4K : cbObject; + else + pThis->cbExtent = RTVFSMEM_MAX_EXTENT_SIZE; +} + + +/** + * Rewinds the file to position 0 and clears the WRITE flag if necessary. + * + * @param pThis The memory file instance. + * @param fFlags The user specified flags. + */ +static void rtVfsMemFileResetAndFixWriteFlag(PRTVFSMEMFILE pThis, uint32_t fFlags) +{ + pThis->pCurExt = RTListGetFirst(&pThis->ExtentHead, RTVFSMEMEXTENT, Entry); + pThis->offCurPos = 0; + + if (!(fFlags & RTFILE_O_WRITE)) + { + /** @todo clear RTFILE_O_WRITE from the resulting. */ + } +} + + +RTDECL(int) RTVfsMemFileCreate(RTVFSIOSTREAM hVfsIos, size_t cbEstimate, PRTVFSFILE phVfsFile) +{ + /* + * Create a memory file instance and set the extension size according to the + * buffer size. Add the WRITE flag so we can use normal write APIs for + * copying the buffer. + */ + RTVFSFILE hVfsFile; + PRTVFSMEMFILE pThis; + int rc = RTVfsNewFile(&g_rtVfsMemFileOps, sizeof(*pThis), RTFILE_O_READ | RTFILE_O_WRITE, NIL_RTVFS, NIL_RTVFSLOCK, + &hVfsFile, (void **)&pThis); + if (RT_SUCCESS(rc)) + { + rtVfsMemInitObjInfo(&pThis->Base.ObjInfo, 0); + rtVfsMemFileInit(pThis, cbEstimate, RTFILE_O_READ | RTFILE_O_WRITE); + + if (hVfsIos != NIL_RTVFSIOSTREAM) + { + RTVFSIOSTREAM hVfsIosDst = RTVfsFileToIoStream(hVfsFile); + rc = RTVfsUtilPumpIoStreams(hVfsIos, hVfsIosDst, pThis->cbExtent); + RTVfsIoStrmRelease(hVfsIosDst); + } + + if (RT_SUCCESS(rc)) + { + *phVfsFile = hVfsFile; + return VINF_SUCCESS; + } + + RTVfsFileRelease(hVfsFile); + } + return rc; +} + + +RTDECL(int) RTVfsMemIoStrmCreate(RTVFSIOSTREAM hVfsIos, size_t cbEstimate, PRTVFSIOSTREAM phVfsIos) +{ + RTVFSFILE hVfsFile; + int rc = RTVfsMemFileCreate(hVfsIos, cbEstimate, &hVfsFile); + if (RT_SUCCESS(rc)) + { + *phVfsIos = RTVfsFileToIoStream(hVfsFile); + AssertStmt(*phVfsIos != NIL_RTVFSIOSTREAM, rc = VERR_INTERNAL_ERROR_2); + RTVfsFileRelease(hVfsFile); + } + return rc; +} + + +RTDECL(int) RTVfsFileFromBuffer(uint32_t fFlags, void const *pvBuf, size_t cbBuf, PRTVFSFILE phVfsFile) +{ + /* + * Create a memory file instance and set the extension size according to the + * buffer size. Add the WRITE flag so we can use normal write APIs for + * copying the buffer. + */ + RTVFSFILE hVfsFile; + PRTVFSMEMFILE pThis; + int rc = RTVfsNewFile(&g_rtVfsMemFileOps, sizeof(*pThis), fFlags | RTFILE_O_WRITE, NIL_RTVFS, NIL_RTVFSLOCK, + &hVfsFile, (void **)&pThis); + if (RT_SUCCESS(rc)) + { + rtVfsMemInitObjInfo(&pThis->Base.ObjInfo, cbBuf); + rtVfsMemFileInit(pThis, cbBuf, fFlags); + + /* + * Copy the buffer and reposition the file pointer to the start. + */ + rc = RTVfsFileWrite(hVfsFile, pvBuf, cbBuf, NULL); + if (RT_SUCCESS(rc)) + { + rtVfsMemFileResetAndFixWriteFlag(pThis, fFlags); + *phVfsFile = hVfsFile; + return VINF_SUCCESS; + } + RTVfsFileRelease(hVfsFile); + } + return rc; +} + + +RTDECL(int) RTVfsIoStrmFromBuffer(uint32_t fFlags, void const *pvBuf, size_t cbBuf, PRTVFSIOSTREAM phVfsIos) +{ + RTVFSFILE hVfsFile; + int rc = RTVfsFileFromBuffer(fFlags, pvBuf, cbBuf, &hVfsFile); + if (RT_SUCCESS(rc)) + { + *phVfsIos = RTVfsFileToIoStream(hVfsFile); + RTVfsFileRelease(hVfsFile); + } + return rc; +} + + +RTDECL(int) RTVfsMemorizeIoStreamAsFile(RTVFSIOSTREAM hVfsIos, uint32_t fFlags, PRTVFSFILE phVfsFile) +{ + /* + * Create a memory file instance and try set the extension size to match + * the length of the I/O stream. + */ + RTFSOBJINFO ObjInfo; + int rc = RTVfsIoStrmQueryInfo(hVfsIos, &ObjInfo, RTFSOBJATTRADD_UNIX); + if (RT_SUCCESS(rc)) + { + RTVFSFILE hVfsFile; + PRTVFSMEMFILE pThis; + rc = RTVfsNewFile(&g_rtVfsMemFileOps, sizeof(*pThis), fFlags | RTFILE_O_WRITE, NIL_RTVFS, NIL_RTVFSLOCK, + &hVfsFile, (void **)&pThis); + if (RT_SUCCESS(rc)) + { + pThis->Base.ObjInfo = ObjInfo; + rtVfsMemFileInit(pThis, ObjInfo.cbObject, fFlags); + + /* + * Copy the stream. + */ + RTVFSIOSTREAM hVfsIosDst = RTVfsFileToIoStream(hVfsFile); + rc = RTVfsUtilPumpIoStreams(hVfsIos, hVfsIosDst, pThis->cbExtent); + RTVfsIoStrmRelease(hVfsIosDst); + if (RT_SUCCESS(rc)) + { + rtVfsMemFileResetAndFixWriteFlag(pThis, fFlags); + *phVfsFile = hVfsFile; + return VINF_SUCCESS; + } + RTVfsFileRelease(hVfsFile); + } + } + return rc; +} + |