vfsmemory.cpp revision 985524f3b29e0f50c596635c16fae80bc78dcc45
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync * IPRT - Virtual File System, Memory Backed VFS.
b72d3233df38e3122eda39b39a27b35c27209615vboxsync * Copyright (C) 2010 Oracle Corporation
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync * available from http://www.virtualbox.org. This file is free software;
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync * you can redistribute it and/or modify it under the terms of the GNU
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync * General Public License (GPL) as published by the Free Software
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync * The contents of this file may alternatively be used under the terms
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync * of the Common Development and Distribution License Version 1.0
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync * VirtualBox OSE distribution, in which case the provisions of the
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync * CDDL are applicable instead of those of the GPL.
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync * You may elect to license modified versions of this file under the
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync * terms and conditions of either the GPL or the CDDL or both.
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync/*******************************************************************************
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync* Header Files *
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync*******************************************************************************/
e2a73964f463b9e91f6f096f9e15974a3edcc416vboxsync/*******************************************************************************
e2a73964f463b9e91f6f096f9e15974a3edcc416vboxsync* Header Files *
e2a73964f463b9e91f6f096f9e15974a3edcc416vboxsync*******************************************************************************/
e2a73964f463b9e91f6f096f9e15974a3edcc416vboxsync/*******************************************************************************
e2a73964f463b9e91f6f096f9e15974a3edcc416vboxsync* Defined Constants And Macros *
e2a73964f463b9e91f6f096f9e15974a3edcc416vboxsync*******************************************************************************/
e2a73964f463b9e91f6f096f9e15974a3edcc416vboxsync/** The max extent size. */
e2a73964f463b9e91f6f096f9e15974a3edcc416vboxsync/*******************************************************************************
e2a73964f463b9e91f6f096f9e15974a3edcc416vboxsync* Structures and Typedefs *
e2a73964f463b9e91f6f096f9e15974a3edcc416vboxsync*******************************************************************************/
0612e2adbcc146b9eb7748983c720e35e38d0dc9vboxsync * Memory base object info.
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsynctypedef struct RTVFSMEMBASE
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync /** The basic object info. */
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync * Memory file extent.
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync * This stores part of the file content.
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync /** Extent list entry. */
e2a73964f463b9e91f6f096f9e15974a3edcc416vboxsync /** The offset of this extent within the file. */
39c2eccedfdb7455c52225543c355e33a65f0c81vboxsync /** The size of the this extent. */
39c2eccedfdb7455c52225543c355e33a65f0c81vboxsync /** The data. */
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync/** Pointer to a memory file extent. */
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync * Memory file.
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsynctypedef struct RTVFSMEMFILE
e2a73964f463b9e91f6f096f9e15974a3edcc416vboxsync /** The base info. */
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync /** The current file position. */
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync /** Pointer to the current file extent. */
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync /** Linked list of file extents - RTVFSMEMEXTENT. */
e2a73964f463b9e91f6f096f9e15974a3edcc416vboxsync /** The current extent size.
0368e9c310393e82ef37c480b6acbd0f107cf0edvboxsync * This is slowly grown to RTVFSMEM_MAX_EXTENT_SIZE as the file grows. */
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync/** Pointer to a memory file. */
d1c36fd86d36726777e3d6f9d040573e0aaf30devboxsync * @interface_method_impl{RTVFSOBJOPS,pfnClose}
d1c36fd86d36726777e3d6f9d040573e0aaf30devboxsyncstatic DECLCALLBACK(int) rtVfsMemFile_Close(void *pvThis)
d1c36fd86d36726777e3d6f9d040573e0aaf30devboxsync * Free the extent list.
d1c36fd86d36726777e3d6f9d040573e0aaf30devboxsync RTListForEachSafe(&pThis->ExtentHead, pCur, pNext, RTVFSMEMEXTENT, Entry)
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsyncstatic DECLCALLBACK(int) rtVfsMemFile_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
d1c36fd86d36726777e3d6f9d040573e0aaf30devboxsync * The slow paths of rtVfsMemFile_LocateExtent.
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsync * @copydoc rtVfsMemFile_LocateExtent
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsyncstatic PRTVFSMEMEXTENT rtVfsMemFile_LocateExtentSlow(PRTVFSMEMFILE pThis, uint64_t off, bool *pfHit)
d1c36fd86d36726777e3d6f9d040573e0aaf30devboxsync * Search from the start or the previously used extent. The heuristics
d1c36fd86d36726777e3d6f9d040573e0aaf30devboxsync * are very very simple, but whatever.
d1c36fd86d36726777e3d6f9d040573e0aaf30devboxsync pExtent = RTListGetFirst(&pThis->ExtentHead, RTVFSMEMEXTENT, Entry);
e2a73964f463b9e91f6f096f9e15974a3edcc416vboxsync PRTVFSMEMEXTENT pNext = RTListGetNext(&pThis->ExtentHead, pExtent, RTVFSMEMEXTENT, Entry);
0612e2adbcc146b9eb7748983c720e35e38d0dc9vboxsync * Locates the extent covering the specified offset, or then one before it.
0612e2adbcc146b9eb7748983c720e35e38d0dc9vboxsync * @returns The closest extent. NULL if off is 0 and there are no extent
0612e2adbcc146b9eb7748983c720e35e38d0dc9vboxsync * covering byte 0 yet.
0612e2adbcc146b9eb7748983c720e35e38d0dc9vboxsync * @param pThis The memory file.
0612e2adbcc146b9eb7748983c720e35e38d0dc9vboxsync * @param off The offset (0-positive).
0612e2adbcc146b9eb7748983c720e35e38d0dc9vboxsync * @param pfHit Where to indicate whether the extent is a
e2a73964f463b9e91f6f096f9e15974a3edcc416vboxsync * direct hit (@c true) or just a closest match
e2a73964f463b9e91f6f096f9e15974a3edcc416vboxsync * (@c false).
e2a73964f463b9e91f6f096f9e15974a3edcc416vboxsyncDECLINLINE(PRTVFSMEMEXTENT) rtVfsMemFile_LocateExtent(PRTVFSMEMFILE pThis, uint64_t off, bool *pfHit)
e2a73964f463b9e91f6f096f9e15974a3edcc416vboxsync * The most likely case is that we're hitting the extent we used in the
e2a73964f463b9e91f6f096f9e15974a3edcc416vboxsync * previous access or the one immediately following it.
e2a73964f463b9e91f6f096f9e15974a3edcc416vboxsync return rtVfsMemFile_LocateExtentSlow(pThis, off, pfHit);
0612e2adbcc146b9eb7748983c720e35e38d0dc9vboxsync pExtent = RTListGetNext(&pThis->ExtentHead, pExtent, RTVFSMEMEXTENT, Entry);
0612e2adbcc146b9eb7748983c720e35e38d0dc9vboxsync return rtVfsMemFile_LocateExtentSlow(pThis, off, pfHit);
b72d3233df38e3122eda39b39a27b35c27209615vboxsync * @interface_method_impl{RTVFSIOSTREAMOPS,pfnRead}
39c2eccedfdb7455c52225543c355e33a65f0c81vboxsyncstatic DECLCALLBACK(int) rtVfsMemFile_Read(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbRead)
39c2eccedfdb7455c52225543c355e33a65f0c81vboxsync * Find the current position and check if it's within the file.
39c2eccedfdb7455c52225543c355e33a65f0c81vboxsync uint64_t offUnsigned = off < 0 ? pThis->offCurPos : (uint64_t)off;
39c2eccedfdb7455c52225543c355e33a65f0c81vboxsync if (offUnsigned >= (uint64_t)pThis->Base.ObjInfo.cbObject)
d8dee9a7ef33d4c705d5fd087d5af9c7cb071f85vboxsync if (offUnsigned + pSgBuf->paSegs[0].cbSeg > (uint64_t)pThis->Base.ObjInfo.cbObject)
d8dee9a7ef33d4c705d5fd087d5af9c7cb071f85vboxsync *pcbRead = cbLeftToRead = (size_t)((uint64_t)pThis->Base.ObjInfo.cbObject - offUnsigned);
d8dee9a7ef33d4c705d5fd087d5af9c7cb071f85vboxsync * Ok, we've got a valid stretch within the file. Do the reading.
d8dee9a7ef33d4c705d5fd087d5af9c7cb071f85vboxsync uint8_t *pbDst = (uint8_t *)pSgBuf->paSegs[0].pvSeg;
d8dee9a7ef33d4c705d5fd087d5af9c7cb071f85vboxsync PRTVFSMEMEXTENT pExtent = rtVfsMemFile_LocateExtent(pThis, offUnsigned, &fHit);
683eff3070b1b86fe71b71af7fda82766ea19d17vboxsync * Do we hit an extent covering the the current file surface?
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync size_t const offExtent = (size_t)(offUnsigned - pExtent->off);
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync memcpy(pbDst, &pExtent->abData[offUnsigned - pExtent->off], cbThisRead);
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync pNext = RTListGetNext(&pThis->ExtentHead, pExtent, RTVFSMEMEXTENT, Entry);
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync * No extent of this portion (sparse file).
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync pNext = RTListGetNext(&pThis->ExtentHead, pExtent, RTVFSMEMEXTENT, Entry);
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync /* Go on and read content from the next extent. */
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync * Allocates a new extent covering the ground at @a offUnsigned.
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync * @returns Pointer to the new extent on success, NULL if we're out of memory.
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync * @param pThis The memory file.
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync * @param offUnsigned The location to allocate the extent at.
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync * @param cbToWrite The number of bytes we're interested in writing
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync * starting at @a offUnsigned.
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync * @param pPrev The extention before @a offUnsigned. NULL if
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsyncstatic PRTVFSMEMEXTENT rtVfsMemFile_AllocExtent(PRTVFSMEMFILE pThis, uint64_t offUnsigned, size_t cbToWrite,
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync * Adjust the extent size if we haven't reached the max size yet.
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync /* Make it a power of two (seeRTVfsMemorizeIoStreamAsFile). */
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync if (((pThis->Base.ObjInfo.cbAllocated + cbNextExtent) & (cbNextExtent - 1)) == 0)
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync * Figure out the size and position of the extent we're adding.
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync uint64_t offExtent = offUnsigned & ~(uint64_t)(pThis->cbExtent - 1);
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync uint64_t const offPrev = pPrev ? pPrev->off + pPrev->cb : 0;
aace59ca7fd1b66174593c5a564878cfac89e11evboxsync ? RTListGetNext(&pThis->ExtentHead, pPrev, RTVFSMEMEXTENT, Entry)
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync : RTListGetFirst(&pThis->ExtentHead, RTVFSMEMEXTENT, Entry);
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync * Allocate, initialize and insert the new extent.
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync PRTVFSMEMEXTENT pNew = (PRTVFSMEMEXTENT)RTMemAllocZ(RT_OFFSETOF(RTVFSMEMEXTENT, abData[cbExtent]));
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync RTListNodeInsertAfter(&pPrev->Entry, &pNew->Entry);
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync /** @todo retry with minimum size. */
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync * @interface_method_impl{RTVFSIOSTREAMOPS,pfnWrite}
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsyncstatic DECLCALLBACK(int) rtVfsMemFile_Write(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbWritten)
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync * Validate the write and set up the write loop.
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync return VINF_SUCCESS; /* pcbWritten is already 0. */
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync uint64_t offUnsigned = off < 0 ? pThis->offCurPos : (uint64_t)off;
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync if (offUnsigned + cbLeftToWrite >= (uint64_t)RTFOFF_MAX)
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync uint8_t const *pbSrc = (uint8_t const *)pSgBuf->paSegs[0].pvSeg;
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync PRTVFSMEMEXTENT pExtent = rtVfsMemFile_LocateExtent(pThis, offUnsigned, &fHit);
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync * If we didn't hit an extent, allocate one (unless it's all zeros).
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync Assert(!pExtent || (pExtent->off < offUnsigned && pExtent->off + pExtent->cb <= offUnsigned));
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync /* Skip leading zeros if there is a whole bunch of them. */
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync uint8_t const *pbSrcNZ = (uint8_t const *)ASMMemIsAll8(pbSrc, cbLeftToWrite, 0);
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync pExtent = rtVfsMemFile_LocateExtent(pThis, offUnsigned, &fHit);
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync pExtent = rtVfsMemFile_AllocExtent(pThis, offUnsigned, cbLeftToWrite, pExtent);
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync * Copy the source data into the current extent.
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync uint32_t const offDst = (uint32_t)(offUnsigned - pExtent->off);
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync memcpy(&pExtent->abData[offDst], pbSrc, cbThisWrite);
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync * Advance to the next extent.
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync PRTVFSMEMEXTENT pNext = RTListGetNext(&pThis->ExtentHead, pExtent, RTVFSMEMEXTENT, Entry);
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync * Update the state, set return value and return.
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync * Note! There must be no alternative exit path from the loop above.
b72d3233df38e3122eda39b39a27b35c27209615vboxsync if ((uint64_t)pThis->Base.ObjInfo.cbObject < offUnsigned)
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync *pcbWritten = pSgBuf->paSegs[0].cbSeg - cbLeftToWrite;
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync * @interface_method_impl{RTVFSIOSTREAMOPS,pfnFlush}
aace59ca7fd1b66174593c5a564878cfac89e11evboxsyncstatic DECLCALLBACK(int) rtVfsMemFile_Flush(void *pvThis)
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync * @interface_method_impl{RTVFSIOSTREAMOPS,pfnPollOne}
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsyncstatic DECLCALLBACK(int) rtVfsMemFile_PollOne(void *pvThis, uint32_t fEvents, RTMSINTERVAL cMillies, bool fIntr,
9b456547aefb8a2e8f5600eba9ec377dbc9b4475vboxsync rc = RTVfsUtilDummyPollOne(fEvents, cMillies, fIntr, pfRetEvents);
b72d3233df38e3122eda39b39a27b35c27209615vboxsync * @interface_method_impl{RTVFSIOSTREAMOPS,pfnTell}
84f746c9015f34e9ab096b87e063d0d6ab7fc7aevboxsyncstatic DECLCALLBACK(int) rtVfsMemFile_Tell(void *pvThis, PRTFOFF poffActual)
return VINF_SUCCESS;
static DECLCALLBACK(int) rtVfsMemFile_SetTimes(void *pvThis, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
if (pAccessTime)
if (pModificationTime)
if (pChangeTime)
if (pBirthTime)
return VINF_SUCCESS;
return VINF_SUCCESS;
static DECLCALLBACK(int) rtVfsMemFile_Seek(void *pvThis, RTFOFF offSeek, unsigned uMethod, PRTFOFF poffActual)
switch (uMethod)
case RTFILE_SEEK_BEGIN:
offWrt = 0;
case RTFILE_SEEK_CURRENT:
case RTFILE_SEEK_END:
return VERR_INTERNAL_ERROR_5;
if (offSeek == 0)
else if (offSeek > 0)
offNew = 0;
return VINF_SUCCESS;
return VINF_SUCCESS;
RTDECL(int) RTVfsMemorizeIoStreamAsFile(RTVFSIOSTREAM hVfsIos, uint32_t fFlags, PRTVFSFILE phVfsFile)
rc = RTVfsNewFile(&g_rtVfsStdFileOps, sizeof(*pThis), fFlags | RTFILE_O_WRITE, NIL_RTVFS, NIL_RTVFSLOCK,
return VINF_SUCCESS;
return rc;