1
/* $Id: vfsmemory.cpp 34560 2010-12-01 11:05:54Z vboxsync $ */
3
* IPRT - Virtual File System, Memory Backed VFS.
7
* Copyright (C) 2010 Oracle Corporation
9
* This file is part of VirtualBox Open Source Edition (OSE), as
10
* available from http://www.virtualbox.org. This file is free software;
11
* you can redistribute it and/or modify it under the terms of the GNU
12
* General Public License (GPL) as published by the Free Software
13
* Foundation, in version 2 as it comes in the "COPYING" file of the
14
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17
* The contents of this file may alternatively be used under the terms
18
* of the Common Development and Distribution License Version 1.0
19
* (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20
* VirtualBox OSE distribution, in which case the provisions of the
21
* CDDL are applicable instead of those of the GPL.
23
* You may elect to license modified versions of this file under the
24
* terms and conditions of either the GPL or the CDDL or both.
28
/*******************************************************************************
30
*******************************************************************************/
31
#include "internal/iprt.h"
35
#include <iprt/assert.h>
37
#include <iprt/file.h>
38
#include <iprt/list.h>
39
#include <iprt/poll.h>
40
#include <iprt/string.h>
41
#include <iprt/vfslowlevel.h>
45
/*******************************************************************************
47
*******************************************************************************/
48
#include "internal/iprt.h"
55
/*******************************************************************************
56
* Defined Constants And Macros *
57
*******************************************************************************/
58
/** The max extent size. */
59
#define RTVFSMEM_MAX_EXTENT_SIZE _2M
62
/*******************************************************************************
63
* Structures and Typedefs *
64
*******************************************************************************/
67
* Memory base object info.
69
typedef struct RTVFSMEMBASE
71
/** The basic object info. */
79
* This stores part of the file content.
81
typedef struct RTVFSMEMEXTENT
83
/** Extent list entry. */
85
/** The offset of this extent within the file. */
87
/** The size of the this extent. */
92
/** Pointer to a memory file extent. */
93
typedef RTVFSMEMEXTENT *PRTVFSMEMEXTENT;
98
typedef struct RTVFSMEMFILE
100
/** The base info. */
102
/** The current file position. */
104
/** Pointer to the current file extent. */
105
PRTVFSMEMEXTENT pCurExt;
106
/** Linked list of file extents - RTVFSMEMEXTENT. */
107
RTLISTNODE ExtentHead;
108
/** The current extent size.
109
* This is slowly grown to RTVFSMEM_MAX_EXTENT_SIZE as the file grows. */
112
/** Pointer to a memory file. */
113
typedef RTVFSMEMFILE *PRTVFSMEMFILE;
118
* @interface_method_impl{RTVFSOBJOPS,pfnClose}
120
static DECLCALLBACK(int) rtVfsMemFile_Close(void *pvThis)
122
PRTVFSMEMFILE pThis = (PRTVFSMEMFILE)pvThis;
125
* Free the extent list.
127
PRTVFSMEMEXTENT pCur, pNext;
128
RTListForEachSafe(&pThis->ExtentHead, pCur, pNext, RTVFSMEMEXTENT, Entry)
130
pCur->off = RTFOFF_MAX;
131
pCur->cb = UINT32_MAX;
132
RTListNodeRemove(&pCur->Entry);
135
pThis->pCurExt = NULL;
142
* @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
144
static DECLCALLBACK(int) rtVfsMemFile_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
146
PRTVFSMEMFILE pThis = (PRTVFSMEMFILE)pvThis;
149
case RTFSOBJATTRADD_NOTHING:
150
case RTFSOBJATTRADD_UNIX:
151
*pObjInfo = pThis->Base.ObjInfo;
155
return VERR_NOT_SUPPORTED;
161
* The slow paths of rtVfsMemFile_LocateExtent.
163
* @copydoc rtVfsMemFile_LocateExtent
165
static PRTVFSMEMEXTENT rtVfsMemFile_LocateExtentSlow(PRTVFSMEMFILE pThis, uint64_t off, bool *pfHit)
168
* Search from the start or the previously used extent. The heuristics
169
* are very very simple, but whatever.
171
PRTVFSMEMEXTENT pExtent = pThis->pCurExt;
172
if (!pExtent || pExtent->off < off)
174
pExtent = RTListGetFirst(&pThis->ExtentHead, RTVFSMEMEXTENT, Entry);
182
while (off - pExtent->off >= pExtent->cb)
184
Assert(pExtent->off <= off);
185
PRTVFSMEMEXTENT pNext = RTListGetNext(&pThis->ExtentHead, pExtent, RTVFSMEMEXTENT, Entry);
197
pThis->pCurExt = pExtent;
203
* Locates the extent covering the specified offset, or then one before it.
205
* @returns The closest extent. NULL if off is 0 and there are no extent
206
* covering byte 0 yet.
207
* @param pThis The memory file.
208
* @param off The offset (0-positive).
209
* @param pfHit Where to indicate whether the extent is a
210
* direct hit (@c true) or just a closest match
213
DECLINLINE(PRTVFSMEMEXTENT) rtVfsMemFile_LocateExtent(PRTVFSMEMFILE pThis, uint64_t off, bool *pfHit)
216
* The most likely case is that we're hitting the extent we used in the
217
* previous access or the one immediately following it.
219
PRTVFSMEMEXTENT pExtent = pThis->pCurExt;
221
return rtVfsMemFile_LocateExtentSlow(pThis, off, pfHit);
223
if (off - pExtent->off >= pExtent->cb)
225
pExtent = RTListGetNext(&pThis->ExtentHead, pExtent, RTVFSMEMEXTENT, Entry);
227
|| off - pExtent->off >= pExtent->cb)
228
return rtVfsMemFile_LocateExtentSlow(pThis, off, pfHit);
229
pThis->pCurExt = pExtent;
238
* @interface_method_impl{RTVFSIOSTREAMOPS,pfnRead}
240
static DECLCALLBACK(int) rtVfsMemFile_Read(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbRead)
242
PRTVFSMEMFILE pThis = (PRTVFSMEMFILE)pvThis;
244
Assert(pSgBuf->cSegs == 1);
249
* Find the current position and check if it's within the file.
251
uint64_t offUnsigned = off < 0 ? pThis->offCurPos : (uint64_t)off;
252
if (offUnsigned >= (uint64_t)pThis->Base.ObjInfo.cbObject)
257
pThis->offCurPos = offUnsigned;
264
if (offUnsigned + pSgBuf->paSegs[0].cbSeg > (uint64_t)pThis->Base.ObjInfo.cbObject)
268
*pcbRead = cbLeftToRead = (size_t)((uint64_t)pThis->Base.ObjInfo.cbObject - offUnsigned);
272
cbLeftToRead = pSgBuf->paSegs[0].cbSeg;
274
*pcbRead = cbLeftToRead;
278
* Ok, we've got a valid stretch within the file. Do the reading.
280
if (cbLeftToRead > 0)
282
uint8_t *pbDst = (uint8_t *)pSgBuf->paSegs[0].pvSeg;
284
PRTVFSMEMEXTENT pExtent = rtVfsMemFile_LocateExtent(pThis, offUnsigned, &fHit);
287
PRTVFSMEMEXTENT pNext;
289
Assert(!pExtent || pExtent->off <= offUnsigned);
292
* Do we hit an extent covering the the current file surface?
296
size_t const offExtent = (size_t)(offUnsigned - pExtent->off);
297
cbThisRead = pExtent->cb - offExtent;
298
if (cbThisRead >= cbLeftToRead)
299
cbThisRead = cbLeftToRead;
301
memcpy(pbDst, &pExtent->abData[offUnsigned - pExtent->off], cbThisRead);
303
offUnsigned += cbThisRead;
304
cbLeftToRead -= cbThisRead;
309
pNext = RTListGetNext(&pThis->ExtentHead, pExtent, RTVFSMEMEXTENT, Entry);
311
&& pNext->off == pExtent->off + pExtent->cb)
320
* No extent of this portion (sparse file).
323
pNext = RTListGetNext(&pThis->ExtentHead, pExtent, RTVFSMEMEXTENT, Entry);
326
Assert(!pNext || pNext->off > pExtent->off);
329
|| offUnsigned + cbLeftToRead <= pNext->off)
330
cbThisRead = cbLeftToRead;
332
cbThisRead = (size_t)(pNext->off - offUnsigned);
334
RT_BZERO(pbDst, cbThisRead);
336
offUnsigned += cbThisRead;
337
cbLeftToRead -= cbThisRead;
342
/* Go on and read content from the next extent. */
348
pThis->offCurPos = offUnsigned;
354
* Allocates a new extent covering the ground at @a offUnsigned.
356
* @returns Pointer to the new extent on success, NULL if we're out of memory.
357
* @param pThis The memory file.
358
* @param offUnsigned The location to allocate the extent at.
359
* @param cbToWrite The number of bytes we're interested in writing
360
* starting at @a offUnsigned.
361
* @param pPrev The extention before @a offUnsigned. NULL if
364
static PRTVFSMEMEXTENT rtVfsMemFile_AllocExtent(PRTVFSMEMFILE pThis, uint64_t offUnsigned, size_t cbToWrite,
365
PRTVFSMEMEXTENT pPrev)
368
* Adjust the extent size if we haven't reached the max size yet.
370
if (pThis->cbExtent != RTVFSMEM_MAX_EXTENT_SIZE)
372
if (cbToWrite >= RTVFSMEM_MAX_EXTENT_SIZE)
373
pThis->cbExtent = RTVFSMEM_MAX_EXTENT_SIZE;
374
else if (!RTListIsEmpty(&pThis->ExtentHead))
376
uint32_t cbNextExtent = pThis->cbExtent;
377
if (RT_IS_POWER_OF_TWO(cbNextExtent))
381
/* Make it a power of two (seeRTVfsMemorizeIoStreamAsFile). */
383
while (cbNextExtent < pThis->cbExtent)
386
if (((pThis->Base.ObjInfo.cbAllocated + cbNextExtent) & (cbNextExtent - 1)) == 0)
387
pThis->cbExtent = cbNextExtent;
392
* Figure out the size and position of the extent we're adding.
394
uint64_t offExtent = offUnsigned & ~(uint64_t)(pThis->cbExtent - 1);
395
uint32_t cbExtent = pThis->cbExtent;
397
uint64_t const offPrev = pPrev ? pPrev->off + pPrev->cb : 0;
398
if (offExtent < offPrev)
401
PRTVFSMEMEXTENT pNext = pPrev
402
? RTListGetNext(&pThis->ExtentHead, pPrev, RTVFSMEMEXTENT, Entry)
403
: RTListGetFirst(&pThis->ExtentHead, RTVFSMEMEXTENT, Entry);
406
uint64_t cbMaxExtent = pNext->off - offExtent;
407
if (cbMaxExtent < cbExtent)
408
cbExtent = (uint32_t)cbMaxExtent;
412
* Allocate, initialize and insert the new extent.
414
PRTVFSMEMEXTENT pNew = (PRTVFSMEMEXTENT)RTMemAllocZ(RT_OFFSETOF(RTVFSMEMEXTENT, abData[cbExtent]));
417
pNew->off = offExtent;
420
RTListNodeInsertAfter(&pPrev->Entry, &pNew->Entry);
422
RTListPrepend(&pThis->ExtentHead, &pNew->Entry);
424
pThis->Base.ObjInfo.cbAllocated += cbExtent;
426
/** @todo retry with minimum size. */
433
* @interface_method_impl{RTVFSIOSTREAMOPS,pfnWrite}
435
static DECLCALLBACK(int) rtVfsMemFile_Write(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbWritten)
437
PRTVFSMEMFILE pThis = (PRTVFSMEMFILE)pvThis;
439
Assert(pSgBuf->cSegs == 1);
444
* Validate the write and set up the write loop.
446
size_t cbLeftToWrite = pSgBuf->paSegs[0].cbSeg;
448
return VINF_SUCCESS; /* pcbWritten is already 0. */
449
uint64_t offUnsigned = off < 0 ? pThis->offCurPos : (uint64_t)off;
450
if (offUnsigned + cbLeftToWrite >= (uint64_t)RTFOFF_MAX)
451
return VERR_OUT_OF_RANGE;
453
int rc = VINF_SUCCESS;
454
uint8_t const *pbSrc = (uint8_t const *)pSgBuf->paSegs[0].pvSeg;
456
PRTVFSMEMEXTENT pExtent = rtVfsMemFile_LocateExtent(pThis, offUnsigned, &fHit);
460
* If we didn't hit an extent, allocate one (unless it's all zeros).
464
Assert(!pExtent || (pExtent->off < offUnsigned && pExtent->off + pExtent->cb <= offUnsigned));
466
/* Skip leading zeros if there is a whole bunch of them. */
467
uint8_t const *pbSrcNZ = (uint8_t const *)ASMMemIsAll8(pbSrc, cbLeftToWrite, 0);
470
offUnsigned += cbLeftToWrite;
474
size_t const cbZeros = pbSrcNZ - pbSrc;
475
if (cbZeros >= RT_MIN(pThis->cbExtent, _64K))
477
offUnsigned += cbZeros;
478
cbLeftToWrite -= cbZeros;
480
pExtent = rtVfsMemFile_LocateExtent(pThis, offUnsigned, &fHit);
485
pExtent = rtVfsMemFile_AllocExtent(pThis, offUnsigned, cbLeftToWrite, pExtent);
494
* Copy the source data into the current extent.
496
uint32_t const offDst = (uint32_t)(offUnsigned - pExtent->off);
497
uint32_t cbThisWrite = pExtent->cb - offDst;
498
if (cbThisWrite > cbLeftToWrite)
499
cbThisWrite = (uint32_t)cbLeftToWrite;
500
memcpy(&pExtent->abData[offDst], pbSrc, cbThisWrite);
502
offUnsigned += cbLeftToWrite;
503
cbLeftToWrite -= cbThisWrite;
506
pbSrc += cbThisWrite;
507
Assert(offUnsigned == pExtent->off + pExtent->cb);
510
* Advance to the next extent.
512
PRTVFSMEMEXTENT pNext = RTListGetNext(&pThis->ExtentHead, pExtent, RTVFSMEMEXTENT, Entry);
513
Assert(!pNext || pNext->off >= offUnsigned);
514
if (pNext && pNext->off == offUnsigned)
521
* Update the state, set return value and return.
522
* Note! There must be no alternative exit path from the loop above.
524
pThis->offCurPos = offUnsigned;
525
if ((uint64_t)pThis->Base.ObjInfo.cbObject < offUnsigned)
526
pThis->Base.ObjInfo.cbObject = offUnsigned;
529
*pcbWritten = pSgBuf->paSegs[0].cbSeg - cbLeftToWrite;
535
* @interface_method_impl{RTVFSIOSTREAMOPS,pfnFlush}
537
static DECLCALLBACK(int) rtVfsMemFile_Flush(void *pvThis)
545
* @interface_method_impl{RTVFSIOSTREAMOPS,pfnPollOne}
547
static DECLCALLBACK(int) rtVfsMemFile_PollOne(void *pvThis, uint32_t fEvents, RTMSINTERVAL cMillies, bool fIntr,
548
uint32_t *pfRetEvents)
550
PRTVFSMEMFILE pThis = (PRTVFSMEMFILE)pvThis;
553
if (fEvents != RTPOLL_EVT_ERROR)
555
*pfRetEvents = fEvents & ~RTPOLL_EVT_ERROR;
559
rc = RTVfsUtilDummyPollOne(fEvents, cMillies, fIntr, pfRetEvents);
565
* @interface_method_impl{RTVFSIOSTREAMOPS,pfnTell}
567
static DECLCALLBACK(int) rtVfsMemFile_Tell(void *pvThis, PRTFOFF poffActual)
569
PRTVFSMEMFILE pThis = (PRTVFSMEMFILE)pvThis;
570
*poffActual = pThis->offCurPos;
576
* @interface_method_impl{RTVFSOBJSETOPS,pfnMode}
578
static DECLCALLBACK(int) rtVfsMemFile_SetMode(void *pvThis, RTFMODE fMode, RTFMODE fMask)
580
PRTVFSMEMFILE pThis = (PRTVFSMEMFILE)pvThis;
581
pThis->Base.ObjInfo.Attr.fMode = (pThis->Base.ObjInfo.Attr.fMode & ~fMask) | fMode;
587
* @interface_method_impl{RTVFSOBJSETOPS,pfnSetTimes}
589
static DECLCALLBACK(int) rtVfsMemFile_SetTimes(void *pvThis, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
590
PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
592
PRTVFSMEMFILE pThis = (PRTVFSMEMFILE)pvThis;
595
pThis->Base.ObjInfo.AccessTime = *pAccessTime;
596
if (pModificationTime)
597
pThis->Base.ObjInfo.ModificationTime = *pModificationTime;
599
pThis->Base.ObjInfo.ChangeTime = *pChangeTime;
601
pThis->Base.ObjInfo.BirthTime = *pBirthTime;
608
* @interface_method_impl{RTVFSOBJSETOPS,pfnSetOwner}
610
static DECLCALLBACK(int) rtVfsMemFile_SetOwner(void *pvThis, RTUID uid, RTGID gid)
612
PRTVFSMEMFILE pThis = (PRTVFSMEMFILE)pvThis;
614
if (uid != NIL_RTUID)
615
pThis->Base.ObjInfo.Attr.u.Unix.uid = uid;
616
if (gid != NIL_RTUID)
617
pThis->Base.ObjInfo.Attr.u.Unix.gid = gid;
624
* @interface_method_impl{RTVFSFILEOPS,pfnSeek}
626
static DECLCALLBACK(int) rtVfsMemFile_Seek(void *pvThis, RTFOFF offSeek, unsigned uMethod, PRTFOFF poffActual)
628
PRTVFSMEMFILE pThis = (PRTVFSMEMFILE)pvThis;
631
* Seek relative to which position.
636
case RTFILE_SEEK_BEGIN:
640
case RTFILE_SEEK_CURRENT:
641
offWrt = pThis->offCurPos;
644
case RTFILE_SEEK_END:
645
offWrt = pThis->Base.ObjInfo.cbObject;
649
return VERR_INTERNAL_ERROR_5;
653
* Calc new position, take care to stay without bounds.
658
else if (offSeek > 0)
660
offNew = offWrt + offSeek;
662
|| offNew > RTFOFF_MAX)
665
else if ((uint64_t)-offSeek < offWrt)
666
offNew = offWrt + offSeek;
671
* Update the state and set return value.
674
&& pThis->pCurExt->off - offNew >= pThis->pCurExt->cb)
675
pThis->pCurExt = NULL;
676
pThis->offCurPos = offNew;
678
*poffActual = offNew;
684
* @interface_method_impl{RTVFSFILEOPS,pfnQuerySize}
686
static DECLCALLBACK(int) rtVfsMemFile_QuerySize(void *pvThis, uint64_t *pcbFile)
688
PRTVFSMEMFILE pThis = (PRTVFSMEMFILE)pvThis;
689
*pcbFile = pThis->Base.ObjInfo.cbObject;
695
* Standard file operations.
697
DECLHIDDEN(const RTVFSFILEOPS) g_rtVfsStdFileOps =
705
rtVfsMemFile_QueryInfo,
708
RTVFSIOSTREAMOPS_VERSION,
709
RTVFSIOSTREAMOPS_FEAT_NO_SG,
713
rtVfsMemFile_PollOne,
717
RTVFSIOSTREAMOPS_VERSION,
719
RTVFSFILEOPS_VERSION,
720
/*RTVFSIOFILEOPS_FEAT_NO_AT_OFFSET*/ 0,
722
RTVFSOBJSETOPS_VERSION,
723
RT_OFFSETOF(RTVFSFILEOPS, Stream.Obj) - RT_OFFSETOF(RTVFSFILEOPS, ObjSet),
724
rtVfsMemFile_SetMode,
725
rtVfsMemFile_SetTimes,
726
rtVfsMemFile_SetOwner,
727
RTVFSOBJSETOPS_VERSION
730
rtVfsMemFile_QuerySize,
738
RTDECL(int) RTVfsMemorizeIoStreamAsFile(RTVFSIOSTREAM hVfsIos, uint32_t fFlags, PRTVFSFILE phVfsFile)
741
* Create a memory file instance and try set the extension size to match
742
* the length of the I/O stream.
745
int rc = RTVfsIoStrmQueryInfo(hVfsIos, &ObjInfo, RTFSOBJATTRADD_UNIX);
750
rc = RTVfsNewFile(&g_rtVfsStdFileOps, sizeof(*pThis), fFlags | RTFILE_O_WRITE, NIL_RTVFS, NIL_RTVFSLOCK,
751
&hVfsFile, (void **)&pThis);
754
pThis->Base.ObjInfo = ObjInfo;
755
pThis->offCurPos = 0;
756
pThis->pCurExt = NULL;
757
RTListInit(&pThis->ExtentHead);
758
if (ObjInfo.cbObject <= 0)
759
pThis->cbExtent = _4K;
760
else if (ObjInfo.cbObject < RTVFSMEM_MAX_EXTENT_SIZE)
761
pThis->cbExtent = _4K /* ObjInfo.cbObject */;
763
pThis->cbExtent = RTVFSMEM_MAX_EXTENT_SIZE;
768
RTVFSIOSTREAM hVfsIosDst = RTVfsFileToIoStream(hVfsFile);
769
rc = RTVfsUtilPumpIoStreams(hVfsIos, hVfsIosDst, pThis->cbExtent);
770
RTVfsIoStrmRelease(hVfsIosDst);
773
pThis->pCurExt = RTListGetFirst(&pThis->ExtentHead, RTVFSMEMEXTENT, Entry);
774
pThis->offCurPos = 0;
776
if (!(fFlags & RTFILE_O_WRITE))
778
/** @todo clear RTFILE_O_WRITE from the resulting. */
780
*phVfsFile = hVfsFile;
783
RTVfsFileRelease(hVfsFile);