VirtualBox

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

Last change on this file since 91095 was 91095, checked in by vboxsync, 4 years ago

IPRT/vfsmemory.cpp: Moved the before-first-extent check down a bit.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 29.0 KB
Line 
1/* $Id: vfsmemory.cpp 91095 2021-09-02 13:51:26Z vboxsync $ */
2/** @file
3 * IPRT - Virtual File System, Memory Backed VFS.
4 */
5
6/*
7 * Copyright (C) 2010-2020 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 || off < pExtent->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 after making sure it is not an
189 offset before the first extent. */
190 pExtent = RTListGetFirst(&pThis->ExtentHead, RTVFSMEMEXTENT, Entry);
191 if (off < pExtent->off)
192 {
193 *pfHit = false;
194 return pExtent;
195 }
196 }
197
198 while (off - pExtent->off >= pExtent->cb)
199 {
200 Assert(pExtent->off <= off);
201 PRTVFSMEMEXTENT pNext = RTListGetNext(&pThis->ExtentHead, pExtent, RTVFSMEMEXTENT, Entry);
202 if ( !pNext
203 || pNext->off > off)
204 {
205 *pfHit = false;
206 return pNext;
207 }
208
209 pExtent = pNext;
210 }
211
212 *pfHit = true;
213 pThis->pCurExt = pExtent;
214 return pExtent;
215}
216
217
218/**
219 * Locates the extent covering the specified offset, or the one after it.
220 *
221 * @returns The closest extent. NULL if off is 0 and there are no extent
222 * covering byte 0 yet.
223 * @param pThis The memory file.
224 * @param off The offset (0-positive).
225 * @param pfHit Where to indicate whether the extent is a
226 * direct hit (@c true) or just a closest match
227 * (@c false).
228 */
229DECLINLINE(PRTVFSMEMEXTENT) rtVfsMemFile_LocateExtent(PRTVFSMEMFILE pThis, uint64_t off, bool *pfHit)
230{
231 /*
232 * The most likely case is that we're hitting the extent we used in the
233 * previous access or the one immediately following it.
234 */
235 PRTVFSMEMEXTENT pExtent = pThis->pCurExt;
236 if (!pExtent)
237 return rtVfsMemFile_LocateExtentSlow(pThis, off, pfHit);
238
239 if (off - pExtent->off >= pExtent->cb)
240 {
241 pExtent = RTListGetNext(&pThis->ExtentHead, pExtent, RTVFSMEMEXTENT, Entry);
242 if ( !pExtent
243 || off - pExtent->off >= pExtent->cb)
244 return rtVfsMemFile_LocateExtentSlow(pThis, off, pfHit);
245 pThis->pCurExt = pExtent;
246 }
247
248 *pfHit = true;
249 return pExtent;
250}
251
252
253/**
254 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnRead}
255 */
256static DECLCALLBACK(int) rtVfsMemFile_Read(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbRead)
257{
258 PRTVFSMEMFILE pThis = (PRTVFSMEMFILE)pvThis;
259
260 Assert(pSgBuf->cSegs == 1);
261 NOREF(fBlocking);
262
263 /*
264 * Find the current position and check if it's within the file.
265 */
266 uint64_t offUnsigned = off < 0 ? pThis->offCurPos : (uint64_t)off;
267 if (offUnsigned >= (uint64_t)pThis->Base.ObjInfo.cbObject)
268 {
269 if (pcbRead)
270 {
271 *pcbRead = 0;
272 pThis->offCurPos = offUnsigned;
273 return VINF_EOF;
274 }
275 return VERR_EOF;
276 }
277
278 size_t cbLeftToRead;
279 if (offUnsigned + pSgBuf->paSegs[0].cbSeg > (uint64_t)pThis->Base.ObjInfo.cbObject)
280 {
281 if (!pcbRead)
282 return VERR_EOF;
283 *pcbRead = cbLeftToRead = (size_t)((uint64_t)pThis->Base.ObjInfo.cbObject - offUnsigned);
284 }
285 else
286 {
287 cbLeftToRead = pSgBuf->paSegs[0].cbSeg;
288 if (pcbRead)
289 *pcbRead = cbLeftToRead;
290 }
291
292 /*
293 * Ok, we've got a valid stretch within the file. Do the reading.
294 */
295 if (cbLeftToRead > 0)
296 {
297 uint8_t *pbDst = (uint8_t *)pSgBuf->paSegs[0].pvSeg;
298 bool fHit;
299 PRTVFSMEMEXTENT pExtent = rtVfsMemFile_LocateExtent(pThis, offUnsigned, &fHit);
300 for (;;)
301 {
302 size_t cbThisRead;
303
304 /*
305 * Do we hit an extent covering the current file surface?
306 */
307 if (fHit)
308 {
309 /* Yes, copy the data. */
310 Assert(offUnsigned - pExtent->off < pExtent->cb);
311 size_t const offExtent = (size_t)(offUnsigned - pExtent->off);
312 cbThisRead = pExtent->cb - offExtent;
313 if (cbThisRead >= cbLeftToRead)
314 cbThisRead = cbLeftToRead;
315
316 memcpy(pbDst, &pExtent->abData[offUnsigned - pExtent->off], cbThisRead);
317
318 offUnsigned += cbThisRead;
319 cbLeftToRead -= cbThisRead;
320 if (!cbLeftToRead)
321 break;
322 pbDst += cbThisRead;
323
324 /* Advance, looping immediately if not sparse. */
325 PRTVFSMEMEXTENT pNext = RTListGetNext(&pThis->ExtentHead, pExtent, RTVFSMEMEXTENT, Entry);
326 if ( pNext
327 && pNext->off == pExtent->off + pExtent->cb)
328 {
329 pExtent = pNext;
330 continue;
331 }
332
333 Assert(!pNext || pNext->off > pExtent->off);
334 pExtent = pNext;
335 fHit = false;
336 }
337 else
338 Assert(!pExtent || pExtent->off > offUnsigned);
339
340 /*
341 * No extent of this portion (sparse file) - Read zeros.
342 */
343 if ( !pExtent
344 || offUnsigned + cbLeftToRead <= pExtent->off)
345 cbThisRead = cbLeftToRead;
346 else
347 cbThisRead = (size_t)(pExtent->off - offUnsigned);
348
349 RT_BZERO(pbDst, cbThisRead);
350
351 offUnsigned += cbThisRead;
352 cbLeftToRead -= cbThisRead;
353 if (!cbLeftToRead)
354 break;
355 pbDst += cbThisRead;
356
357 /* Go on and read content from the next extent. */
358 fHit = true;
359 }
360 }
361
362 pThis->offCurPos = offUnsigned;
363 return VINF_SUCCESS;
364}
365
366
367/**
368 * Allocates a new extent covering the ground at @a offUnsigned.
369 *
370 * @returns Pointer to the new extent on success, NULL if we're out of memory.
371 * @param pThis The memory file.
372 * @param offUnsigned The location to allocate the extent at.
373 * @param cbToWrite The number of bytes we're interested in writing
374 * starting at @a offUnsigned.
375 * @param pNext The extention after @a offUnsigned. NULL if
376 * none, i.e. we're allocating space at the end of
377 * the file.
378 */
379static PRTVFSMEMEXTENT rtVfsMemFile_AllocExtent(PRTVFSMEMFILE pThis, uint64_t offUnsigned, size_t cbToWrite,
380 PRTVFSMEMEXTENT pNext)
381{
382 /*
383 * Adjust the extent size if we haven't reached the max size yet.
384 */
385 if (pThis->cbExtent != RTVFSMEM_MAX_EXTENT_SIZE)
386 {
387 if (cbToWrite >= RTVFSMEM_MAX_EXTENT_SIZE)
388 pThis->cbExtent = RTVFSMEM_MAX_EXTENT_SIZE;
389 else if (!RTListIsEmpty(&pThis->ExtentHead))
390 {
391 uint32_t cbNextExtent = pThis->cbExtent;
392 if (RT_IS_POWER_OF_TWO(cbNextExtent))
393 cbNextExtent *= 2;
394 else
395 {
396 /* Make it a power of two (seeRTVfsMemorizeIoStreamAsFile). */
397 cbNextExtent = _4K;
398 while (cbNextExtent < pThis->cbExtent)
399 cbNextExtent *= 2;
400 }
401 if (((pThis->Base.ObjInfo.cbAllocated + cbNextExtent) & (cbNextExtent - 1)) == 0)
402 pThis->cbExtent = cbNextExtent;
403 }
404 }
405
406 /*
407 * Figure out the size and position of the extent we're adding.
408 */
409 uint64_t offExtent = offUnsigned & ~(uint64_t)(pThis->cbExtent - 1);
410 uint32_t cbExtent = pThis->cbExtent;
411
412 PRTVFSMEMEXTENT pPrev = pNext
413 ? RTListGetPrev(&pThis->ExtentHead, pNext, RTVFSMEMEXTENT, Entry)
414 : RTListGetLast(&pThis->ExtentHead, RTVFSMEMEXTENT, Entry);
415 uint64_t const offPrev = pPrev ? pPrev->off + pPrev->cb : 0;
416 if (offExtent < offPrev)
417 offExtent = offPrev;
418
419 if (pNext)
420 {
421 uint64_t cbMaxExtent = pNext->off - offExtent;
422 if (cbMaxExtent < cbExtent)
423 cbExtent = (uint32_t)cbMaxExtent;
424 }
425
426 /*
427 * Allocate, initialize and insert the new extent.
428 */
429 PRTVFSMEMEXTENT pNew = (PRTVFSMEMEXTENT)RTMemAllocZ(RT_UOFFSETOF_DYN(RTVFSMEMEXTENT, abData[cbExtent]));
430 if (pNew)
431 {
432 pNew->off = offExtent;
433 pNew->cb = cbExtent;
434 if (pPrev)
435 RTListNodeInsertAfter(&pPrev->Entry, &pNew->Entry);
436 else
437 RTListPrepend(&pThis->ExtentHead, &pNew->Entry);
438
439 pThis->Base.ObjInfo.cbAllocated += cbExtent;
440 }
441 /** @todo retry with minimum size. */
442
443 return pNew;
444}
445
446
447/**
448 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnWrite}
449 */
450static DECLCALLBACK(int) rtVfsMemFile_Write(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbWritten)
451{
452 PRTVFSMEMFILE pThis = (PRTVFSMEMFILE)pvThis;
453
454 Assert(pSgBuf->cSegs == 1);
455 NOREF(fBlocking);
456
457 /*
458 * Validate the write and set up the write loop.
459 */
460 size_t cbLeftToWrite = pSgBuf->paSegs[0].cbSeg;
461 if (!cbLeftToWrite)
462 return VINF_SUCCESS; /* pcbWritten is already 0. */
463 uint64_t offUnsigned = off < 0 ? pThis->offCurPos : (uint64_t)off;
464 if (offUnsigned + cbLeftToWrite >= (uint64_t)RTFOFF_MAX)
465 return VERR_OUT_OF_RANGE;
466
467 int rc = VINF_SUCCESS;
468 uint8_t const *pbSrc = (uint8_t const *)pSgBuf->paSegs[0].pvSeg;
469 bool fHit;
470 PRTVFSMEMEXTENT pExtent = rtVfsMemFile_LocateExtent(pThis, offUnsigned, &fHit);
471 for (;;)
472 {
473 /*
474 * If we didn't hit an extent, allocate one (unless it's all zeros).
475 */
476 if (!fHit)
477 {
478 Assert(!pExtent || pExtent->off > offUnsigned);
479
480 /* Skip leading zeros if there is a whole bunch of them. */
481 uint8_t const *pbSrcNZ = (uint8_t const *)ASMMemFirstNonZero(pbSrc, cbLeftToWrite);
482 size_t cbZeros = pbSrcNZ ? pbSrcNZ - pbSrc : cbLeftToWrite;
483 if (cbZeros)
484 {
485 uint64_t const cbToNext = pExtent ? pExtent->off - offUnsigned : UINT64_MAX;
486 if (cbZeros > cbToNext)
487 cbZeros = (size_t)cbToNext;
488 offUnsigned += cbZeros;
489 cbLeftToWrite -= cbZeros;
490 if (!cbLeftToWrite)
491 break;
492 pbSrc += cbZeros;
493
494 Assert(!pExtent || offUnsigned <= pExtent->off);
495 if (pExtent && pExtent->off == offUnsigned)
496 {
497 fHit = true;
498 continue;
499 }
500 }
501
502 fHit = true;
503 pExtent = rtVfsMemFile_AllocExtent(pThis, offUnsigned, cbLeftToWrite, pExtent);
504 if (!pExtent)
505 {
506 rc = VERR_NO_MEMORY;
507 break;
508 }
509 }
510 Assert(offUnsigned - pExtent->off < pExtent->cb);
511
512 /*
513 * Copy the source data into the current extent.
514 */
515 uint32_t const offDst = (uint32_t)(offUnsigned - pExtent->off);
516 uint32_t cbThisWrite = pExtent->cb - offDst;
517 if (cbThisWrite > cbLeftToWrite)
518 cbThisWrite = (uint32_t)cbLeftToWrite;
519 memcpy(&pExtent->abData[offDst], pbSrc, cbThisWrite);
520
521 offUnsigned += cbThisWrite;
522 cbLeftToWrite -= cbThisWrite;
523 if (!cbLeftToWrite)
524 break;
525 pbSrc += cbThisWrite;
526 Assert(offUnsigned == pExtent->off + pExtent->cb);
527
528 /*
529 * Advance to the next extent (emulate the lookup).
530 */
531 pExtent = RTListGetNext(&pThis->ExtentHead, pExtent, RTVFSMEMEXTENT, Entry);
532 fHit = pExtent && (offUnsigned - pExtent->off < pExtent->cb);
533 }
534
535 /*
536 * Update the state, set return value and return.
537 * Note! There must be no alternative exit path from the loop above.
538 */
539 pThis->offCurPos = offUnsigned;
540 if ((uint64_t)pThis->Base.ObjInfo.cbObject < offUnsigned)
541 pThis->Base.ObjInfo.cbObject = offUnsigned;
542
543 if (pcbWritten)
544 *pcbWritten = pSgBuf->paSegs[0].cbSeg - cbLeftToWrite;
545 return rc;
546}
547
548
549/**
550 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnFlush}
551 */
552static DECLCALLBACK(int) rtVfsMemFile_Flush(void *pvThis)
553{
554 NOREF(pvThis);
555 return VINF_SUCCESS;
556}
557
558
559/**
560 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnTell}
561 */
562static DECLCALLBACK(int) rtVfsMemFile_Tell(void *pvThis, PRTFOFF poffActual)
563{
564 PRTVFSMEMFILE pThis = (PRTVFSMEMFILE)pvThis;
565 *poffActual = pThis->offCurPos;
566 return VINF_SUCCESS;
567}
568
569
570/**
571 * @interface_method_impl{RTVFSOBJSETOPS,pfnMode}
572 */
573static DECLCALLBACK(int) rtVfsMemFile_SetMode(void *pvThis, RTFMODE fMode, RTFMODE fMask)
574{
575 PRTVFSMEMFILE pThis = (PRTVFSMEMFILE)pvThis;
576 pThis->Base.ObjInfo.Attr.fMode = (pThis->Base.ObjInfo.Attr.fMode & ~fMask) | fMode;
577 return VINF_SUCCESS;
578}
579
580
581/**
582 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetTimes}
583 */
584static DECLCALLBACK(int) rtVfsMemFile_SetTimes(void *pvThis, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
585 PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
586{
587 PRTVFSMEMFILE pThis = (PRTVFSMEMFILE)pvThis;
588
589 if (pAccessTime)
590 pThis->Base.ObjInfo.AccessTime = *pAccessTime;
591 if (pModificationTime)
592 pThis->Base.ObjInfo.ModificationTime = *pModificationTime;
593 if (pChangeTime)
594 pThis->Base.ObjInfo.ChangeTime = *pChangeTime;
595 if (pBirthTime)
596 pThis->Base.ObjInfo.BirthTime = *pBirthTime;
597
598 return VINF_SUCCESS;
599}
600
601
602/**
603 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetOwner}
604 */
605static DECLCALLBACK(int) rtVfsMemFile_SetOwner(void *pvThis, RTUID uid, RTGID gid)
606{
607 PRTVFSMEMFILE pThis = (PRTVFSMEMFILE)pvThis;
608
609 if (uid != NIL_RTUID)
610 pThis->Base.ObjInfo.Attr.u.Unix.uid = uid;
611 if (gid != NIL_RTUID)
612 pThis->Base.ObjInfo.Attr.u.Unix.gid = gid;
613
614 return VINF_SUCCESS;
615}
616
617
618/**
619 * @interface_method_impl{RTVFSFILEOPS,pfnSeek}
620 */
621static DECLCALLBACK(int) rtVfsMemFile_Seek(void *pvThis, RTFOFF offSeek, unsigned uMethod, PRTFOFF poffActual)
622{
623 PRTVFSMEMFILE pThis = (PRTVFSMEMFILE)pvThis;
624
625 /*
626 * Seek relative to which position.
627 */
628 uint64_t offWrt;
629 switch (uMethod)
630 {
631 case RTFILE_SEEK_BEGIN:
632 offWrt = 0;
633 break;
634
635 case RTFILE_SEEK_CURRENT:
636 offWrt = pThis->offCurPos;
637 break;
638
639 case RTFILE_SEEK_END:
640 offWrt = pThis->Base.ObjInfo.cbObject;
641 break;
642
643 default:
644 return VERR_INTERNAL_ERROR_5;
645 }
646
647 /*
648 * Calc new position, take care to stay within RTFOFF type bounds.
649 */
650 uint64_t offNew;
651 if (offSeek == 0)
652 offNew = offWrt;
653 else if (offSeek > 0)
654 {
655 offNew = offWrt + offSeek;
656 if ( offNew < offWrt
657 || offNew > RTFOFF_MAX)
658 offNew = RTFOFF_MAX;
659 }
660 else if ((uint64_t)-offSeek < offWrt)
661 offNew = offWrt + offSeek;
662 else
663 offNew = 0;
664
665 /*
666 * Update the state and set return value.
667 */
668 if ( pThis->pCurExt
669 && pThis->pCurExt->off - offNew >= pThis->pCurExt->cb)
670 pThis->pCurExt = NULL;
671 pThis->offCurPos = offNew;
672
673 *poffActual = offNew;
674 return VINF_SUCCESS;
675}
676
677
678/**
679 * @interface_method_impl{RTVFSFILEOPS,pfnQuerySize}
680 */
681static DECLCALLBACK(int) rtVfsMemFile_QuerySize(void *pvThis, uint64_t *pcbFile)
682{
683 PRTVFSMEMFILE pThis = (PRTVFSMEMFILE)pvThis;
684 *pcbFile = pThis->Base.ObjInfo.cbObject;
685 return VINF_SUCCESS;
686}
687
688
689/**
690 * @interface_method_impl{RTVFSFILEOPS,pfnSetSize}
691 */
692static DECLCALLBACK(int) rtVfsMemFile_SetSize(void *pvThis, uint64_t cbFile, uint32_t fFlags)
693{
694 NOREF(pvThis); NOREF(cbFile); NOREF(fFlags);
695 AssertMsgFailed(("Lucky you! You get to implement this (or bug bird about it).\n"));
696 return VERR_NOT_IMPLEMENTED;
697}
698
699
700/**
701 * @interface_method_impl{RTVFSFILEOPS,pfnQueryMaxSize}
702 */
703static DECLCALLBACK(int) rtVfsMemFile_QueryMaxSize(void *pvThis, uint64_t *pcbMax)
704{
705 RT_NOREF(pvThis);
706 *pcbMax = ~(size_t)0 >> 1;
707 return VINF_SUCCESS;
708}
709
710
711/**
712 * Memory file operations.
713 */
714DECL_HIDDEN_CONST(const RTVFSFILEOPS) g_rtVfsMemFileOps =
715{
716 { /* Stream */
717 { /* Obj */
718 RTVFSOBJOPS_VERSION,
719 RTVFSOBJTYPE_FILE,
720 "MemFile",
721 rtVfsMemFile_Close,
722 rtVfsMemFile_QueryInfo,
723 RTVFSOBJOPS_VERSION
724 },
725 RTVFSIOSTREAMOPS_VERSION,
726 RTVFSIOSTREAMOPS_FEAT_NO_SG,
727 rtVfsMemFile_Read,
728 rtVfsMemFile_Write,
729 rtVfsMemFile_Flush,
730 NULL /*PollOne*/,
731 rtVfsMemFile_Tell,
732 NULL /*Skip*/,
733 NULL /*ZeroFill*/,
734 RTVFSIOSTREAMOPS_VERSION,
735 },
736 RTVFSFILEOPS_VERSION,
737 /*RTVFSIOFILEOPS_FEAT_NO_AT_OFFSET*/ 0,
738 { /* ObjSet */
739 RTVFSOBJSETOPS_VERSION,
740 RT_UOFFSETOF(RTVFSFILEOPS, ObjSet) - RT_UOFFSETOF(RTVFSFILEOPS, Stream.Obj),
741 rtVfsMemFile_SetMode,
742 rtVfsMemFile_SetTimes,
743 rtVfsMemFile_SetOwner,
744 RTVFSOBJSETOPS_VERSION
745 },
746 rtVfsMemFile_Seek,
747 rtVfsMemFile_QuerySize,
748 rtVfsMemFile_SetSize,
749 rtVfsMemFile_QueryMaxSize,
750 RTVFSFILEOPS_VERSION
751};
752
753
754/**
755 * Initialize the RTVFSMEMFILE::Base.ObjInfo specific members.
756 *
757 * @param pObjInfo The object info to init.
758 * @param cbObject The object size set.
759 */
760static void rtVfsMemInitObjInfo(PRTFSOBJINFO pObjInfo, uint64_t cbObject)
761{
762 pObjInfo->cbObject = cbObject;
763 pObjInfo->cbAllocated = cbObject;
764 pObjInfo->Attr.fMode = RTFS_DOS_NT_NORMAL | RTFS_TYPE_FILE | RTFS_UNIX_IRWXU;
765 pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX;
766 pObjInfo->Attr.u.Unix.uid = NIL_RTUID;
767 pObjInfo->Attr.u.Unix.gid = NIL_RTGID;
768 pObjInfo->Attr.u.Unix.cHardlinks = 1;
769 pObjInfo->Attr.u.Unix.INodeIdDevice = 0;
770 pObjInfo->Attr.u.Unix.INodeId = 0;
771 pObjInfo->Attr.u.Unix.fFlags = 0;
772 pObjInfo->Attr.u.Unix.GenerationId = 0;
773 pObjInfo->Attr.u.Unix.Device = 0;
774 RTTimeNow(&pObjInfo->AccessTime);
775 pObjInfo->ModificationTime = pObjInfo->AccessTime;
776 pObjInfo->ChangeTime = pObjInfo->AccessTime;
777 pObjInfo->BirthTime = pObjInfo->AccessTime;
778}
779
780
781/**
782 * Initialize the RTVFSMEMFILE specific members.
783 *
784 * @param pThis The memory file to initialize.
785 * @param cbObject The object size for estimating extent size.
786 * @param fFlags The user specified flags.
787 */
788static void rtVfsMemFileInit(PRTVFSMEMFILE pThis, RTFOFF cbObject, uint32_t fFlags)
789{
790 pThis->offCurPos = 0;
791 pThis->pCurExt = NULL;
792 RTListInit(&pThis->ExtentHead);
793 if (cbObject <= 0)
794 pThis->cbExtent = _4K;
795 else if (cbObject < RTVFSMEM_MAX_EXTENT_SIZE)
796 pThis->cbExtent = fFlags & RTFILE_O_WRITE ? _4K : cbObject;
797 else
798 pThis->cbExtent = RTVFSMEM_MAX_EXTENT_SIZE;
799}
800
801
802/**
803 * Rewinds the file to position 0 and clears the WRITE flag if necessary.
804 *
805 * @param pThis The memory file instance.
806 * @param fFlags The user specified flags.
807 */
808static void rtVfsMemFileResetAndFixWriteFlag(PRTVFSMEMFILE pThis, uint32_t fFlags)
809{
810 pThis->pCurExt = RTListGetFirst(&pThis->ExtentHead, RTVFSMEMEXTENT, Entry);
811 pThis->offCurPos = 0;
812
813 if (!(fFlags & RTFILE_O_WRITE))
814 {
815 /** @todo clear RTFILE_O_WRITE from the resulting. */
816 }
817}
818
819
820RTDECL(int) RTVfsMemFileCreate(RTVFSIOSTREAM hVfsIos, size_t cbEstimate, PRTVFSFILE phVfsFile)
821{
822 /*
823 * Create a memory file instance and set the extension size according to the
824 * buffer size. Add the WRITE flag so we can use normal write APIs for
825 * copying the buffer.
826 */
827 RTVFSFILE hVfsFile;
828 PRTVFSMEMFILE pThis;
829 int rc = RTVfsNewFile(&g_rtVfsMemFileOps, sizeof(*pThis), RTFILE_O_READ | RTFILE_O_WRITE, NIL_RTVFS, NIL_RTVFSLOCK,
830 &hVfsFile, (void **)&pThis);
831 if (RT_SUCCESS(rc))
832 {
833 rtVfsMemInitObjInfo(&pThis->Base.ObjInfo, 0);
834 rtVfsMemFileInit(pThis, cbEstimate, RTFILE_O_READ | RTFILE_O_WRITE);
835
836 if (hVfsIos != NIL_RTVFSIOSTREAM)
837 {
838 RTVFSIOSTREAM hVfsIosDst = RTVfsFileToIoStream(hVfsFile);
839 rc = RTVfsUtilPumpIoStreams(hVfsIos, hVfsIosDst, pThis->cbExtent);
840 RTVfsIoStrmRelease(hVfsIosDst);
841 }
842
843 if (RT_SUCCESS(rc))
844 {
845 *phVfsFile = hVfsFile;
846 return VINF_SUCCESS;
847 }
848
849 RTVfsFileRelease(hVfsFile);
850 }
851 return rc;
852}
853
854
855RTDECL(int) RTVfsMemIoStrmCreate(RTVFSIOSTREAM hVfsIos, size_t cbEstimate, PRTVFSIOSTREAM phVfsIos)
856{
857 RTVFSFILE hVfsFile;
858 int rc = RTVfsMemFileCreate(hVfsIos, cbEstimate, &hVfsFile);
859 if (RT_SUCCESS(rc))
860 {
861 *phVfsIos = RTVfsFileToIoStream(hVfsFile);
862 AssertStmt(*phVfsIos != NIL_RTVFSIOSTREAM, rc = VERR_INTERNAL_ERROR_2);
863 RTVfsFileRelease(hVfsFile);
864 }
865 return rc;
866}
867
868
869RTDECL(int) RTVfsFileFromBuffer(uint32_t fFlags, void const *pvBuf, size_t cbBuf, PRTVFSFILE phVfsFile)
870{
871 /*
872 * Create a memory file instance and set the extension size according to the
873 * buffer size. Add the WRITE flag so we can use normal write APIs for
874 * copying the buffer.
875 */
876 RTVFSFILE hVfsFile;
877 PRTVFSMEMFILE pThis;
878 int rc = RTVfsNewFile(&g_rtVfsMemFileOps, sizeof(*pThis), fFlags | RTFILE_O_WRITE, NIL_RTVFS, NIL_RTVFSLOCK,
879 &hVfsFile, (void **)&pThis);
880 if (RT_SUCCESS(rc))
881 {
882 rtVfsMemInitObjInfo(&pThis->Base.ObjInfo, cbBuf);
883 rtVfsMemFileInit(pThis, cbBuf, fFlags);
884
885 /*
886 * Copy the buffer and reposition the file pointer to the start.
887 */
888 rc = RTVfsFileWrite(hVfsFile, pvBuf, cbBuf, NULL);
889 if (RT_SUCCESS(rc))
890 {
891 rtVfsMemFileResetAndFixWriteFlag(pThis, fFlags);
892 *phVfsFile = hVfsFile;
893 return VINF_SUCCESS;
894 }
895 RTVfsFileRelease(hVfsFile);
896 }
897 return rc;
898}
899
900
901RTDECL(int) RTVfsIoStrmFromBuffer(uint32_t fFlags, void const *pvBuf, size_t cbBuf, PRTVFSIOSTREAM phVfsIos)
902{
903 RTVFSFILE hVfsFile;
904 int rc = RTVfsFileFromBuffer(fFlags, pvBuf, cbBuf, &hVfsFile);
905 if (RT_SUCCESS(rc))
906 {
907 *phVfsIos = RTVfsFileToIoStream(hVfsFile);
908 RTVfsFileRelease(hVfsFile);
909 }
910 return rc;
911}
912
913
914RTDECL(int) RTVfsMemorizeIoStreamAsFile(RTVFSIOSTREAM hVfsIos, uint32_t fFlags, PRTVFSFILE phVfsFile)
915{
916 /*
917 * Create a memory file instance and try set the extension size to match
918 * the length of the I/O stream.
919 */
920 RTFSOBJINFO ObjInfo;
921 int rc = RTVfsIoStrmQueryInfo(hVfsIos, &ObjInfo, RTFSOBJATTRADD_UNIX);
922 if (RT_SUCCESS(rc))
923 {
924 RTVFSFILE hVfsFile;
925 PRTVFSMEMFILE pThis;
926 rc = RTVfsNewFile(&g_rtVfsMemFileOps, sizeof(*pThis), fFlags | RTFILE_O_WRITE, NIL_RTVFS, NIL_RTVFSLOCK,
927 &hVfsFile, (void **)&pThis);
928 if (RT_SUCCESS(rc))
929 {
930 pThis->Base.ObjInfo = ObjInfo;
931 rtVfsMemFileInit(pThis, ObjInfo.cbObject, fFlags);
932
933 /*
934 * Copy the stream.
935 */
936 RTVFSIOSTREAM hVfsIosDst = RTVfsFileToIoStream(hVfsFile);
937 rc = RTVfsUtilPumpIoStreams(hVfsIos, hVfsIosDst, pThis->cbExtent);
938 RTVfsIoStrmRelease(hVfsIosDst);
939 if (RT_SUCCESS(rc))
940 {
941 rtVfsMemFileResetAndFixWriteFlag(pThis, fFlags);
942 *phVfsFile = hVfsFile;
943 return VINF_SUCCESS;
944 }
945 RTVfsFileRelease(hVfsFile);
946 }
947 }
948 return rc;
949}
950
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