VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/vfs/vfsmemory.cpp@ 62220

Last change on this file since 62220 was 59747, checked in by vboxsync, 9 years ago

iprt/asm.h: Cleaned up the ASMMemIsAll8/U32 mess and implmeneted the former in assembly. (Found inverted usage due to bad naming in copyUtf8Block, but it is fortunately an unused method.) Replaces the complicated ASMBitFirstSet based scanning in RTSgBufIsZero with a simple call to the new ASMMemIsZero function.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 27.0 KB
Line 
1/* $Id: vfsmemory.cpp 59747 2016-02-19 23:18:18Z vboxsync $ */
2/** @file
3 * IPRT - Virtual File System, Memory Backed VFS.
4 */
5
6/*
7 * Copyright (C) 2010-2015 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. 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.
16 *
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.
22 *
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.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#include "internal/iprt.h"
32#include <iprt/vfs.h>
33
34#include <iprt/asm.h>
35#include <iprt/assert.h>
36#include <iprt/err.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>
42
43
44
45/*********************************************************************************************************************************
46* Header Files *
47*********************************************************************************************************************************/
48#include "internal/iprt.h"
49#include <iprt/vfs.h>
50
51#include <iprt/err.h>
52#include <iprt/mem.h>
53
54
55/*********************************************************************************************************************************
56* Defined Constants And Macros *
57*********************************************************************************************************************************/
58/** The max extent size. */
59#define RTVFSMEM_MAX_EXTENT_SIZE _2M
60
61
62/*********************************************************************************************************************************
63* Structures and Typedefs *
64*********************************************************************************************************************************/
65
66/**
67 * Memory base object info.
68 */
69typedef struct RTVFSMEMBASE
70{
71 /** The basic object info. */
72 RTFSOBJINFO ObjInfo;
73} RTVFSMEMBASE;
74
75
76/**
77 * Memory file extent.
78 *
79 * This stores part of the file content.
80 */
81typedef struct RTVFSMEMEXTENT
82{
83 /** Extent list entry. */
84 RTLISTNODE Entry;
85 /** The offset of this extent within the file. */
86 uint64_t off;
87 /** The size of the this extent. */
88 uint32_t cb;
89 /** The data. */
90 uint8_t abData[1];
91} RTVFSMEMEXTENT;
92/** Pointer to a memory file extent. */
93typedef RTVFSMEMEXTENT *PRTVFSMEMEXTENT;
94
95/**
96 * Memory file.
97 */
98typedef struct RTVFSMEMFILE
99{
100 /** The base info. */
101 RTVFSMEMBASE Base;
102 /** The current file position. */
103 uint64_t offCurPos;
104 /** Pointer to the current file extent. */
105 PRTVFSMEMEXTENT pCurExt;
106 /** Linked list of file extents - RTVFSMEMEXTENT. */
107 RTLISTANCHOR ExtentHead;
108 /** The current extent size.
109 * This is slowly grown to RTVFSMEM_MAX_EXTENT_SIZE as the file grows. */
110 uint32_t cbExtent;
111} RTVFSMEMFILE;
112/** Pointer to a memory file. */
113typedef RTVFSMEMFILE *PRTVFSMEMFILE;
114
115
116
117/**
118 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
119 */
120static DECLCALLBACK(int) rtVfsMemFile_Close(void *pvThis)
121{
122 PRTVFSMEMFILE pThis = (PRTVFSMEMFILE)pvThis;
123
124 /*
125 * Free the extent list.
126 */
127 PRTVFSMEMEXTENT pCur, pNext;
128 RTListForEachSafe(&pThis->ExtentHead, pCur, pNext, RTVFSMEMEXTENT, Entry)
129 {
130 pCur->off = RTFOFF_MAX;
131 pCur->cb = UINT32_MAX;
132 RTListNodeRemove(&pCur->Entry);
133 RTMemFree(pCur);
134 }
135 pThis->pCurExt = NULL;
136
137 return VINF_SUCCESS;
138}
139
140
141/**
142 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
143 */
144static DECLCALLBACK(int) rtVfsMemFile_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
145{
146 PRTVFSMEMFILE pThis = (PRTVFSMEMFILE)pvThis;
147 switch (enmAddAttr)
148 {
149 case RTFSOBJATTRADD_NOTHING:
150 case RTFSOBJATTRADD_UNIX:
151 *pObjInfo = pThis->Base.ObjInfo;
152 return VINF_SUCCESS;
153
154 default:
155 return VERR_NOT_SUPPORTED;
156 }
157}
158
159
160/**
161 * The slow paths of rtVfsMemFile_LocateExtent.
162 *
163 * @copydoc rtVfsMemFile_LocateExtent
164 */
165static PRTVFSMEMEXTENT rtVfsMemFile_LocateExtentSlow(PRTVFSMEMFILE pThis, uint64_t off, bool *pfHit)
166{
167 /*
168 * Search from the start or the previously used extent. The heuristics
169 * are very very simple, but whatever.
170 */
171 PRTVFSMEMEXTENT pExtent = pThis->pCurExt;
172 if (!pExtent || pExtent->off < off)
173 {
174 /* Consider the last entry first (for writes). */
175 pExtent = RTListGetLast(&pThis->ExtentHead, RTVFSMEMEXTENT, Entry);
176 if (!pExtent)
177 {
178 *pfHit = false;
179 return NULL;
180 }
181 if (off - pExtent->off < pExtent->cb)
182 {
183 *pfHit = true;
184 pThis->pCurExt = pExtent;
185 return pExtent;
186 }
187
188 /* Otherwise, start from the head. */
189 pExtent = RTListGetFirst(&pThis->ExtentHead, RTVFSMEMEXTENT, Entry);
190 }
191
192 while (off - pExtent->off >= pExtent->cb)
193 {
194 Assert(pExtent->off <= off);
195 PRTVFSMEMEXTENT pNext = RTListGetNext(&pThis->ExtentHead, pExtent, RTVFSMEMEXTENT, Entry);
196 if ( !pNext
197 || pNext->off > off)
198 {
199 *pfHit = false;
200 return pNext;
201 }
202
203 pExtent = pNext;
204 }
205
206 *pfHit = true;
207 pThis->pCurExt = pExtent;
208 return pExtent;
209}
210
211
212/**
213 * Locates the extent covering the specified offset, or the one after it.
214 *
215 * @returns The closest extent. NULL if off is 0 and there are no extent
216 * covering byte 0 yet.
217 * @param pThis The memory file.
218 * @param off The offset (0-positive).
219 * @param pfHit Where to indicate whether the extent is a
220 * direct hit (@c true) or just a closest match
221 * (@c false).
222 */
223DECLINLINE(PRTVFSMEMEXTENT) rtVfsMemFile_LocateExtent(PRTVFSMEMFILE pThis, uint64_t off, bool *pfHit)
224{
225 /*
226 * The most likely case is that we're hitting the extent we used in the
227 * previous access or the one immediately following it.
228 */
229 PRTVFSMEMEXTENT pExtent = pThis->pCurExt;
230 if (!pExtent)
231 return rtVfsMemFile_LocateExtentSlow(pThis, off, pfHit);
232
233 if (off - pExtent->off >= pExtent->cb)
234 {
235 pExtent = RTListGetNext(&pThis->ExtentHead, pExtent, RTVFSMEMEXTENT, Entry);
236 if ( !pExtent
237 || off - pExtent->off >= pExtent->cb)
238 return rtVfsMemFile_LocateExtentSlow(pThis, off, pfHit);
239 pThis->pCurExt = pExtent;
240 }
241
242 *pfHit = true;
243 return pExtent;
244}
245
246
247/**
248 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnRead}
249 */
250static DECLCALLBACK(int) rtVfsMemFile_Read(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbRead)
251{
252 PRTVFSMEMFILE pThis = (PRTVFSMEMFILE)pvThis;
253
254 Assert(pSgBuf->cSegs == 1);
255 Assert(off < 0);
256 NOREF(fBlocking);
257
258 /*
259 * Find the current position and check if it's within the file.
260 */
261 uint64_t offUnsigned = off < 0 ? pThis->offCurPos : (uint64_t)off;
262 if (offUnsigned >= (uint64_t)pThis->Base.ObjInfo.cbObject)
263 {
264 if (pcbRead)
265 {
266 *pcbRead = 0;
267 pThis->offCurPos = offUnsigned;
268 return VINF_EOF;
269 }
270 return VERR_EOF;
271 }
272
273 size_t cbLeftToRead;
274 if (offUnsigned + pSgBuf->paSegs[0].cbSeg > (uint64_t)pThis->Base.ObjInfo.cbObject)
275 {
276 if (!pcbRead)
277 return VERR_EOF;
278 *pcbRead = cbLeftToRead = (size_t)((uint64_t)pThis->Base.ObjInfo.cbObject - offUnsigned);
279 }
280 else
281 {
282 cbLeftToRead = pSgBuf->paSegs[0].cbSeg;
283 if (pcbRead)
284 *pcbRead = cbLeftToRead;
285 }
286
287 /*
288 * Ok, we've got a valid stretch within the file. Do the reading.
289 */
290 if (cbLeftToRead > 0)
291 {
292 uint8_t *pbDst = (uint8_t *)pSgBuf->paSegs[0].pvSeg;
293 bool fHit;
294 PRTVFSMEMEXTENT pExtent = rtVfsMemFile_LocateExtent(pThis, offUnsigned, &fHit);
295 for (;;)
296 {
297 size_t cbThisRead;
298
299 /*
300 * Do we hit an extent covering the current file surface?
301 */
302 if (fHit)
303 {
304 /* Yes, copy the data. */
305 Assert(offUnsigned - pExtent->off < pExtent->cb);
306 size_t const offExtent = (size_t)(offUnsigned - pExtent->off);
307 cbThisRead = pExtent->cb - offExtent;
308 if (cbThisRead >= cbLeftToRead)
309 cbThisRead = cbLeftToRead;
310
311 memcpy(pbDst, &pExtent->abData[offUnsigned - pExtent->off], cbThisRead);
312
313 offUnsigned += cbThisRead;
314 cbLeftToRead -= cbThisRead;
315 if (!cbLeftToRead)
316 break;
317 pbDst += cbThisRead;
318
319 /* Advance, looping immediately if not sparse. */
320 PRTVFSMEMEXTENT pNext = RTListGetNext(&pThis->ExtentHead, pExtent, RTVFSMEMEXTENT, Entry);
321 if ( pNext
322 && pNext->off == pExtent->off + pExtent->cb)
323 {
324 pExtent = pNext;
325 continue;
326 }
327
328 Assert(!pNext || pNext->off > pExtent->off);
329 pExtent = pNext;
330 fHit = false;
331 }
332 else
333 Assert(!pExtent || pExtent->off > offUnsigned);
334
335 /*
336 * No extent of this portion (sparse file) - Read zeros.
337 */
338 if ( !pExtent
339 || offUnsigned + cbLeftToRead <= pExtent->off)
340 cbThisRead = cbLeftToRead;
341 else
342 cbThisRead = (size_t)(pExtent->off - offUnsigned);
343
344 RT_BZERO(pbDst, cbThisRead);
345
346 offUnsigned += cbThisRead;
347 cbLeftToRead -= cbThisRead;
348 if (!cbLeftToRead)
349 break;
350 pbDst += cbThisRead;
351
352 /* Go on and read content from the next extent. */
353 fHit = true;
354 }
355 }
356
357 pThis->offCurPos = offUnsigned;
358 return VINF_SUCCESS;
359}
360
361
362/**
363 * Allocates a new extent covering the ground at @a offUnsigned.
364 *
365 * @returns Pointer to the new extent on success, NULL if we're out of memory.
366 * @param pThis The memory file.
367 * @param offUnsigned The location to allocate the extent at.
368 * @param cbToWrite The number of bytes we're interested in writing
369 * starting at @a offUnsigned.
370 * @param pNext The extention after @a offUnsigned. NULL if
371 * none, i.e. we're allocating space at the end of
372 * the file.
373 */
374static PRTVFSMEMEXTENT rtVfsMemFile_AllocExtent(PRTVFSMEMFILE pThis, uint64_t offUnsigned, size_t cbToWrite,
375 PRTVFSMEMEXTENT pNext)
376{
377 /*
378 * Adjust the extent size if we haven't reached the max size yet.
379 */
380 if (pThis->cbExtent != RTVFSMEM_MAX_EXTENT_SIZE)
381 {
382 if (cbToWrite >= RTVFSMEM_MAX_EXTENT_SIZE)
383 pThis->cbExtent = RTVFSMEM_MAX_EXTENT_SIZE;
384 else if (!RTListIsEmpty(&pThis->ExtentHead))
385 {
386 uint32_t cbNextExtent = pThis->cbExtent;
387 if (RT_IS_POWER_OF_TWO(cbNextExtent))
388 cbNextExtent *= 2;
389 else
390 {
391 /* Make it a power of two (seeRTVfsMemorizeIoStreamAsFile). */
392 cbNextExtent = _4K;
393 while (cbNextExtent < pThis->cbExtent)
394 cbNextExtent *= 2;
395 }
396 if (((pThis->Base.ObjInfo.cbAllocated + cbNextExtent) & (cbNextExtent - 1)) == 0)
397 pThis->cbExtent = cbNextExtent;
398 }
399 }
400
401 /*
402 * Figure out the size and position of the extent we're adding.
403 */
404 uint64_t offExtent = offUnsigned & ~(uint64_t)(pThis->cbExtent - 1);
405 uint32_t cbExtent = pThis->cbExtent;
406
407 PRTVFSMEMEXTENT pPrev = pNext
408 ? RTListGetPrev(&pThis->ExtentHead, pNext, RTVFSMEMEXTENT, Entry)
409 : RTListGetLast(&pThis->ExtentHead, RTVFSMEMEXTENT, Entry);
410 uint64_t const offPrev = pPrev ? pPrev->off + pPrev->cb : 0;
411 if (offExtent < offPrev)
412 offExtent = offPrev;
413
414 if (pNext)
415 {
416 uint64_t cbMaxExtent = pNext->off - offExtent;
417 if (cbMaxExtent < cbExtent)
418 cbExtent = (uint32_t)cbMaxExtent;
419 }
420
421 /*
422 * Allocate, initialize and insert the new extent.
423 */
424 PRTVFSMEMEXTENT pNew = (PRTVFSMEMEXTENT)RTMemAllocZ(RT_OFFSETOF(RTVFSMEMEXTENT, abData[cbExtent]));
425 if (pNew)
426 {
427 pNew->off = offExtent;
428 pNew->cb = cbExtent;
429 if (pPrev)
430 RTListNodeInsertAfter(&pPrev->Entry, &pNew->Entry);
431 else
432 RTListPrepend(&pThis->ExtentHead, &pNew->Entry);
433
434 pThis->Base.ObjInfo.cbAllocated += cbExtent;
435 }
436 /** @todo retry with minimum size. */
437
438 return pNew;
439}
440
441
442/**
443 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnWrite}
444 */
445static DECLCALLBACK(int) rtVfsMemFile_Write(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbWritten)
446{
447 PRTVFSMEMFILE pThis = (PRTVFSMEMFILE)pvThis;
448
449 Assert(pSgBuf->cSegs == 1);
450 Assert(off < 0);
451 NOREF(fBlocking);
452
453 /*
454 * Validate the write and set up the write loop.
455 */
456 size_t cbLeftToWrite = pSgBuf->paSegs[0].cbSeg;
457 if (!cbLeftToWrite)
458 return VINF_SUCCESS; /* pcbWritten is already 0. */
459 uint64_t offUnsigned = off < 0 ? pThis->offCurPos : (uint64_t)off;
460 if (offUnsigned + cbLeftToWrite >= (uint64_t)RTFOFF_MAX)
461 return VERR_OUT_OF_RANGE;
462
463 int rc = VINF_SUCCESS;
464 uint8_t const *pbSrc = (uint8_t const *)pSgBuf->paSegs[0].pvSeg;
465 bool fHit;
466 PRTVFSMEMEXTENT pExtent = rtVfsMemFile_LocateExtent(pThis, offUnsigned, &fHit);
467 for (;;)
468 {
469 /*
470 * If we didn't hit an extent, allocate one (unless it's all zeros).
471 */
472 if (!fHit)
473 {
474 Assert(!pExtent || pExtent->off > offUnsigned);
475
476 /* Skip leading zeros if there is a whole bunch of them. */
477 uint8_t const *pbSrcNZ = (uint8_t const *)ASMMemFirstNonZero(pbSrc, cbLeftToWrite);
478 size_t cbZeros = pbSrcNZ ? pbSrcNZ - pbSrc : cbLeftToWrite;
479 if (cbZeros)
480 {
481 uint64_t const cbToNext = pExtent ? pExtent->off - offUnsigned : UINT64_MAX;
482 if (cbZeros > cbToNext)
483 cbZeros = (size_t)cbToNext;
484 offUnsigned += cbZeros;
485 cbLeftToWrite -= cbZeros;
486 if (!cbLeftToWrite)
487 break;
488
489 Assert(!pExtent || offUnsigned <= pExtent->off);
490 if (pExtent && pExtent->off == offUnsigned)
491 {
492 fHit = true;
493 continue;
494 }
495 }
496
497 fHit = true;
498 pExtent = rtVfsMemFile_AllocExtent(pThis, offUnsigned, cbLeftToWrite, pExtent);
499 if (!pExtent)
500 {
501 rc = VERR_NO_MEMORY;
502 break;
503 }
504 }
505 Assert(offUnsigned - pExtent->off < pExtent->cb);
506
507 /*
508 * Copy the source data into the current extent.
509 */
510 uint32_t const offDst = (uint32_t)(offUnsigned - pExtent->off);
511 uint32_t cbThisWrite = pExtent->cb - offDst;
512 if (cbThisWrite > cbLeftToWrite)
513 cbThisWrite = (uint32_t)cbLeftToWrite;
514 memcpy(&pExtent->abData[offDst], pbSrc, cbThisWrite);
515
516 offUnsigned += cbLeftToWrite;
517 cbLeftToWrite -= cbThisWrite;
518 if (!cbLeftToWrite)
519 break;
520 pbSrc += cbThisWrite;
521 Assert(offUnsigned == pExtent->off + pExtent->cb);
522
523 /*
524 * Advance to the next extent (emulate the lookup).
525 */
526 pExtent = RTListGetNext(&pThis->ExtentHead, pExtent, RTVFSMEMEXTENT, Entry);
527 fHit = pExtent && (offUnsigned - pExtent->off < pExtent->cb);
528 }
529
530 /*
531 * Update the state, set return value and return.
532 * Note! There must be no alternative exit path from the loop above.
533 */
534 pThis->offCurPos = offUnsigned;
535 if ((uint64_t)pThis->Base.ObjInfo.cbObject < offUnsigned)
536 pThis->Base.ObjInfo.cbObject = offUnsigned;
537
538 if (pcbWritten)
539 *pcbWritten = pSgBuf->paSegs[0].cbSeg - cbLeftToWrite;
540 return rc;
541}
542
543
544/**
545 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnFlush}
546 */
547static DECLCALLBACK(int) rtVfsMemFile_Flush(void *pvThis)
548{
549 NOREF(pvThis);
550 return VINF_SUCCESS;
551}
552
553
554/**
555 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnPollOne}
556 */
557static DECLCALLBACK(int) rtVfsMemFile_PollOne(void *pvThis, uint32_t fEvents, RTMSINTERVAL cMillies, bool fIntr,
558 uint32_t *pfRetEvents)
559{
560 NOREF(pvThis);
561 int rc;
562 if (fEvents != RTPOLL_EVT_ERROR)
563 {
564 *pfRetEvents = fEvents & ~RTPOLL_EVT_ERROR;
565 rc = VINF_SUCCESS;
566 }
567 else
568 rc = RTVfsUtilDummyPollOne(fEvents, cMillies, fIntr, pfRetEvents);
569 return rc;
570}
571
572
573/**
574 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnTell}
575 */
576static DECLCALLBACK(int) rtVfsMemFile_Tell(void *pvThis, PRTFOFF poffActual)
577{
578 PRTVFSMEMFILE pThis = (PRTVFSMEMFILE)pvThis;
579 *poffActual = pThis->offCurPos;
580 return VINF_SUCCESS;
581}
582
583
584/**
585 * @interface_method_impl{RTVFSOBJSETOPS,pfnMode}
586 */
587static DECLCALLBACK(int) rtVfsMemFile_SetMode(void *pvThis, RTFMODE fMode, RTFMODE fMask)
588{
589 PRTVFSMEMFILE pThis = (PRTVFSMEMFILE)pvThis;
590 pThis->Base.ObjInfo.Attr.fMode = (pThis->Base.ObjInfo.Attr.fMode & ~fMask) | fMode;
591 return VINF_SUCCESS;
592}
593
594
595/**
596 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetTimes}
597 */
598static DECLCALLBACK(int) rtVfsMemFile_SetTimes(void *pvThis, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
599 PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
600{
601 PRTVFSMEMFILE pThis = (PRTVFSMEMFILE)pvThis;
602
603 if (pAccessTime)
604 pThis->Base.ObjInfo.AccessTime = *pAccessTime;
605 if (pModificationTime)
606 pThis->Base.ObjInfo.ModificationTime = *pModificationTime;
607 if (pChangeTime)
608 pThis->Base.ObjInfo.ChangeTime = *pChangeTime;
609 if (pBirthTime)
610 pThis->Base.ObjInfo.BirthTime = *pBirthTime;
611
612 return VINF_SUCCESS;
613}
614
615
616/**
617 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetOwner}
618 */
619static DECLCALLBACK(int) rtVfsMemFile_SetOwner(void *pvThis, RTUID uid, RTGID gid)
620{
621 PRTVFSMEMFILE pThis = (PRTVFSMEMFILE)pvThis;
622
623 if (uid != NIL_RTUID)
624 pThis->Base.ObjInfo.Attr.u.Unix.uid = uid;
625 if (gid != NIL_RTUID)
626 pThis->Base.ObjInfo.Attr.u.Unix.gid = gid;
627
628 return VINF_SUCCESS;
629}
630
631
632/**
633 * @interface_method_impl{RTVFSFILEOPS,pfnSeek}
634 */
635static DECLCALLBACK(int) rtVfsMemFile_Seek(void *pvThis, RTFOFF offSeek, unsigned uMethod, PRTFOFF poffActual)
636{
637 PRTVFSMEMFILE pThis = (PRTVFSMEMFILE)pvThis;
638
639 /*
640 * Seek relative to which position.
641 */
642 uint64_t offWrt;
643 switch (uMethod)
644 {
645 case RTFILE_SEEK_BEGIN:
646 offWrt = 0;
647 break;
648
649 case RTFILE_SEEK_CURRENT:
650 offWrt = pThis->offCurPos;
651 break;
652
653 case RTFILE_SEEK_END:
654 offWrt = pThis->Base.ObjInfo.cbObject;
655 break;
656
657 default:
658 return VERR_INTERNAL_ERROR_5;
659 }
660
661 /*
662 * Calc new position, take care to stay without bounds.
663 */
664 uint64_t offNew;
665 if (offSeek == 0)
666 offNew = offWrt;
667 else if (offSeek > 0)
668 {
669 offNew = offWrt + offSeek;
670 if ( offNew < offWrt
671 || offNew > RTFOFF_MAX)
672 offNew = RTFOFF_MAX;
673 }
674 else if ((uint64_t)-offSeek < offWrt)
675 offNew = offWrt + offSeek;
676 else
677 offNew = 0;
678
679 /*
680 * Update the state and set return value.
681 */
682 if ( pThis->pCurExt
683 && pThis->pCurExt->off - offNew >= pThis->pCurExt->cb)
684 pThis->pCurExt = NULL;
685 pThis->offCurPos = offNew;
686
687 *poffActual = offNew;
688 return VINF_SUCCESS;
689}
690
691
692/**
693 * @interface_method_impl{RTVFSFILEOPS,pfnQuerySize}
694 */
695static DECLCALLBACK(int) rtVfsMemFile_QuerySize(void *pvThis, uint64_t *pcbFile)
696{
697 PRTVFSMEMFILE pThis = (PRTVFSMEMFILE)pvThis;
698 *pcbFile = pThis->Base.ObjInfo.cbObject;
699 return VINF_SUCCESS;
700}
701
702
703/**
704 * Memory file operations.
705 */
706DECL_HIDDEN_CONST(const RTVFSFILEOPS) g_rtVfsMemFileOps =
707{
708 { /* Stream */
709 { /* Obj */
710 RTVFSOBJOPS_VERSION,
711 RTVFSOBJTYPE_FILE,
712 "MemFile",
713 rtVfsMemFile_Close,
714 rtVfsMemFile_QueryInfo,
715 RTVFSOBJOPS_VERSION
716 },
717 RTVFSIOSTREAMOPS_VERSION,
718 RTVFSIOSTREAMOPS_FEAT_NO_SG,
719 rtVfsMemFile_Read,
720 rtVfsMemFile_Write,
721 rtVfsMemFile_Flush,
722 rtVfsMemFile_PollOne,
723 rtVfsMemFile_Tell,
724 NULL /*Skip*/,
725 NULL /*ZeroFill*/,
726 RTVFSIOSTREAMOPS_VERSION,
727 },
728 RTVFSFILEOPS_VERSION,
729 /*RTVFSIOFILEOPS_FEAT_NO_AT_OFFSET*/ 0,
730 { /* ObjSet */
731 RTVFSOBJSETOPS_VERSION,
732 RT_OFFSETOF(RTVFSFILEOPS, Stream.Obj) - RT_OFFSETOF(RTVFSFILEOPS, ObjSet),
733 rtVfsMemFile_SetMode,
734 rtVfsMemFile_SetTimes,
735 rtVfsMemFile_SetOwner,
736 RTVFSOBJSETOPS_VERSION
737 },
738 rtVfsMemFile_Seek,
739 rtVfsMemFile_QuerySize,
740 RTVFSFILEOPS_VERSION
741};
742
743
744/**
745 * Initialize the RTVFSMEMFILE specific members.
746 *
747 * @param pThis The memory file to initialize.
748 * @param cbObject The object size for estimating extent size.
749 * @param fFlags The user specified flags.
750 */
751static void rtVfsMemFileInit(PRTVFSMEMFILE pThis, RTFOFF cbObject, uint32_t fFlags)
752{
753 pThis->offCurPos = 0;
754 pThis->pCurExt = NULL;
755 RTListInit(&pThis->ExtentHead);
756 if (cbObject <= 0)
757 pThis->cbExtent = _4K;
758 else if (cbObject < RTVFSMEM_MAX_EXTENT_SIZE)
759 pThis->cbExtent = fFlags & RTFILE_O_WRITE ? _4K : cbObject;
760 else
761 pThis->cbExtent = RTVFSMEM_MAX_EXTENT_SIZE;
762}
763
764
765/**
766 * Rewinds the file to position 0 and clears the WRITE flag if necessary.
767 *
768 * @param pThis The memory file instance.
769 * @param fFlags The user specified flags.
770 */
771static void rtVfsMemFileResetAndFixWriteFlag(PRTVFSMEMFILE pThis, uint32_t fFlags)
772{
773 pThis->pCurExt = RTListGetFirst(&pThis->ExtentHead, RTVFSMEMEXTENT, Entry);
774 pThis->offCurPos = 0;
775
776 if (!(fFlags & RTFILE_O_WRITE))
777 {
778 /** @todo clear RTFILE_O_WRITE from the resulting. */
779 }
780}
781
782
783RTDECL(int) RTVfsMemFileCreate(RTVFSIOSTREAM hVfsIos, size_t cbEstimate, PRTVFSFILE phVfsFile)
784{
785 /*
786 * Create a memory file instance and set the extension size according to the
787 * buffer size. Add the WRITE flag so we can use normal write APIs for
788 * copying the buffer.
789 */
790 RTVFSFILE hVfsFile;
791 PRTVFSMEMFILE pThis;
792 int rc = RTVfsNewFile(&g_rtVfsMemFileOps, sizeof(*pThis), RTFILE_O_READ | RTFILE_O_WRITE, NIL_RTVFS, NIL_RTVFSLOCK,
793 &hVfsFile, (void **)&pThis);
794 if (RT_SUCCESS(rc))
795 {
796 pThis->Base.ObjInfo.cbObject = 0;
797 pThis->Base.ObjInfo.Attr.fMode = RTFS_DOS_NT_NORMAL | RTFS_TYPE_FILE | RTFS_UNIX_IRWXU;
798 rtVfsMemFileInit(pThis, cbEstimate, RTFILE_O_READ | RTFILE_O_WRITE);
799
800 *phVfsFile = hVfsFile;
801 return VINF_SUCCESS;
802 }
803 return rc;
804}
805
806
807RTDECL(int) RTVfsFileFromBuffer(uint32_t fFlags, void const *pvBuf, size_t cbBuf, PRTVFSFILE phVfsFile)
808{
809 /*
810 * Create a memory file instance and set the extension size according to the
811 * buffer size. Add the WRITE flag so we can use normal write APIs for
812 * copying the buffer.
813 */
814 RTVFSFILE hVfsFile;
815 PRTVFSMEMFILE pThis;
816 int rc = RTVfsNewFile(&g_rtVfsMemFileOps, sizeof(*pThis), fFlags | RTFILE_O_WRITE, NIL_RTVFS, NIL_RTVFSLOCK,
817 &hVfsFile, (void **)&pThis);
818 if (RT_SUCCESS(rc))
819 {
820 pThis->Base.ObjInfo.cbObject = cbBuf;
821 pThis->Base.ObjInfo.Attr.fMode = RTFS_DOS_NT_NORMAL | RTFS_TYPE_FILE | RTFS_UNIX_IRWXU;
822 rtVfsMemFileInit(pThis, cbBuf, fFlags);
823
824 /*
825 * Copy the buffer and reposition the file pointer to the start.
826 */
827 rc = RTVfsFileWrite(hVfsFile, pvBuf, cbBuf, NULL);
828 if (RT_SUCCESS(rc))
829 {
830 rtVfsMemFileResetAndFixWriteFlag(pThis, fFlags);
831 *phVfsFile = hVfsFile;
832 return VINF_SUCCESS;
833 }
834 RTVfsFileRelease(hVfsFile);
835 }
836 return rc;
837}
838
839
840RTDECL(int) RTVfsIoStrmFromBuffer(uint32_t fFlags, void const *pvBuf, size_t cbBuf, PRTVFSIOSTREAM phVfsIos)
841{
842 RTVFSFILE hVfsFile;
843 int rc = RTVfsFileFromBuffer(fFlags, pvBuf, cbBuf, &hVfsFile);
844 if (RT_SUCCESS(rc))
845 {
846 *phVfsIos = RTVfsFileToIoStream(hVfsFile);
847 RTVfsFileRelease(hVfsFile);
848 }
849 return rc;
850}
851
852
853RTDECL(int) RTVfsMemorizeIoStreamAsFile(RTVFSIOSTREAM hVfsIos, uint32_t fFlags, PRTVFSFILE phVfsFile)
854{
855 /*
856 * Create a memory file instance and try set the extension size to match
857 * the length of the I/O stream.
858 */
859 RTFSOBJINFO ObjInfo;
860 int rc = RTVfsIoStrmQueryInfo(hVfsIos, &ObjInfo, RTFSOBJATTRADD_UNIX);
861 if (RT_SUCCESS(rc))
862 {
863 RTVFSFILE hVfsFile;
864 PRTVFSMEMFILE pThis;
865 rc = RTVfsNewFile(&g_rtVfsMemFileOps, sizeof(*pThis), fFlags | RTFILE_O_WRITE, NIL_RTVFS, NIL_RTVFSLOCK,
866 &hVfsFile, (void **)&pThis);
867 if (RT_SUCCESS(rc))
868 {
869 pThis->Base.ObjInfo = ObjInfo;
870 rtVfsMemFileInit(pThis, ObjInfo.cbObject, fFlags);
871
872 /*
873 * Copy the stream.
874 */
875 RTVFSIOSTREAM hVfsIosDst = RTVfsFileToIoStream(hVfsFile);
876 rc = RTVfsUtilPumpIoStreams(hVfsIos, hVfsIosDst, pThis->cbExtent);
877 RTVfsIoStrmRelease(hVfsIosDst);
878 if (RT_SUCCESS(rc))
879 {
880 rtVfsMemFileResetAndFixWriteFlag(pThis, fFlags);
881 *phVfsFile = hVfsFile;
882 return VINF_SUCCESS;
883 }
884 RTVfsFileRelease(hVfsFile);
885 }
886 }
887 return rc;
888}
889
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette