VirtualBox

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

Last change on this file was 103005, checked in by vboxsync, 4 months ago

iprt/asm.h,*: Split out the ASMMem* and related stuff into a separate header, asm-mem.h, so that we can get the RT_ASM_PAGE_SIZE stuff out of the way.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 29.8 KB
Line 
1/* $Id: vfsmemory.cpp 103005 2024-01-23 23:55:58Z vboxsync $ */
2/** @file
3 * IPRT - Virtual File System, Memory Backed VFS.
4 */
5
6/*
7 * Copyright (C) 2010-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
27 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
28 * in the VirtualBox distribution, in which case the provisions of the
29 * CDDL are applicable instead of those of the GPL.
30 *
31 * You may elect to license modified versions of this file under the
32 * terms and conditions of either the GPL or the CDDL or both.
33 *
34 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
35 */
36
37
38/*********************************************************************************************************************************
39* Header Files *
40*********************************************************************************************************************************/
41#include "internal/iprt.h"
42#include <iprt/vfs.h>
43
44#include <iprt/asm-mem.h>
45#include <iprt/assert.h>
46#include <iprt/err.h>
47#include <iprt/file.h>
48#include <iprt/list.h>
49#include <iprt/poll.h>
50#include <iprt/string.h>
51#include <iprt/vfslowlevel.h>
52
53
54
55/*********************************************************************************************************************************
56* Header Files *
57*********************************************************************************************************************************/
58#include "internal/iprt.h"
59#include <iprt/vfs.h>
60
61#include <iprt/err.h>
62#include <iprt/mem.h>
63
64
65/*********************************************************************************************************************************
66* Defined Constants And Macros *
67*********************************************************************************************************************************/
68/** The max extent size. */
69#define RTVFSMEM_MAX_EXTENT_SIZE _2M
70
71
72/*********************************************************************************************************************************
73* Structures and Typedefs *
74*********************************************************************************************************************************/
75
76/**
77 * Memory base object info.
78 */
79typedef struct RTVFSMEMBASE
80{
81 /** The basic object info. */
82 RTFSOBJINFO ObjInfo;
83} RTVFSMEMBASE;
84
85
86/**
87 * Memory file extent.
88 *
89 * This stores part of the file content.
90 */
91typedef struct RTVFSMEMEXTENT
92{
93 /** Extent list entry. */
94 RTLISTNODE Entry;
95 /** The offset of this extent within the file. */
96 uint64_t off;
97 /** The size of the this extent. */
98 uint32_t cb;
99 /** The data. */
100 uint8_t abData[1];
101} RTVFSMEMEXTENT;
102/** Pointer to a memory file extent. */
103typedef RTVFSMEMEXTENT *PRTVFSMEMEXTENT;
104
105/**
106 * Memory file.
107 */
108typedef struct RTVFSMEMFILE
109{
110 /** The base info. */
111 RTVFSMEMBASE Base;
112 /** The current file position. */
113 uint64_t offCurPos;
114 /** Pointer to the current file extent. */
115 PRTVFSMEMEXTENT pCurExt;
116 /** Linked list of file extents - RTVFSMEMEXTENT. */
117 RTLISTANCHOR ExtentHead;
118 /** The current extent size.
119 * This is slowly grown to RTVFSMEM_MAX_EXTENT_SIZE as the file grows. */
120 uint32_t cbExtent;
121} RTVFSMEMFILE;
122/** Pointer to a memory file. */
123typedef RTVFSMEMFILE *PRTVFSMEMFILE;
124
125
126
127/**
128 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
129 */
130static DECLCALLBACK(int) rtVfsMemFile_Close(void *pvThis)
131{
132 PRTVFSMEMFILE pThis = (PRTVFSMEMFILE)pvThis;
133
134 /*
135 * Free the extent list.
136 */
137 PRTVFSMEMEXTENT pCur, pNext;
138 RTListForEachSafe(&pThis->ExtentHead, pCur, pNext, RTVFSMEMEXTENT, Entry)
139 {
140 pCur->off = RTFOFF_MAX;
141 pCur->cb = UINT32_MAX;
142 RTListNodeRemove(&pCur->Entry);
143 RTMemFree(pCur);
144 }
145 pThis->pCurExt = NULL;
146
147 return VINF_SUCCESS;
148}
149
150
151/**
152 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
153 */
154static DECLCALLBACK(int) rtVfsMemFile_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
155{
156 PRTVFSMEMFILE pThis = (PRTVFSMEMFILE)pvThis;
157 switch (enmAddAttr)
158 {
159 case RTFSOBJATTRADD_NOTHING:
160 case RTFSOBJATTRADD_UNIX:
161 *pObjInfo = pThis->Base.ObjInfo;
162 return VINF_SUCCESS;
163
164 default:
165 return VERR_NOT_SUPPORTED;
166 }
167}
168
169
170/**
171 * The slow paths of rtVfsMemFile_LocateExtent.
172 *
173 * @copydoc rtVfsMemFile_LocateExtent
174 */
175static PRTVFSMEMEXTENT rtVfsMemFile_LocateExtentSlow(PRTVFSMEMFILE pThis, uint64_t off, bool *pfHit)
176{
177 /*
178 * Search from the start or the previously used extent. The heuristics
179 * are very very simple, but whatever.
180 */
181 PRTVFSMEMEXTENT pExtent = pThis->pCurExt;
182 if (!pExtent || off < pExtent->off)
183 {
184 /* Consider the last entry first (for writes). */
185 pExtent = RTListGetLast(&pThis->ExtentHead, RTVFSMEMEXTENT, Entry);
186 if (!pExtent)
187 {
188 *pfHit = false;
189 return NULL;
190 }
191 if (off - pExtent->off < pExtent->cb)
192 {
193 *pfHit = true;
194 pThis->pCurExt = pExtent;
195 return pExtent;
196 }
197
198 /* Otherwise, start from the head after making sure it is not an
199 offset before the first extent. */
200 pExtent = RTListGetFirst(&pThis->ExtentHead, RTVFSMEMEXTENT, Entry);
201 if (off < pExtent->off)
202 {
203 *pfHit = false;
204 return pExtent;
205 }
206 }
207
208 while (off - pExtent->off >= pExtent->cb)
209 {
210 Assert(pExtent->off <= off);
211 PRTVFSMEMEXTENT pNext = RTListGetNext(&pThis->ExtentHead, pExtent, RTVFSMEMEXTENT, Entry);
212 if ( !pNext
213 || pNext->off > off)
214 {
215 *pfHit = false;
216 return pNext;
217 }
218
219 pExtent = pNext;
220 }
221
222 *pfHit = true;
223 pThis->pCurExt = pExtent;
224 return pExtent;
225}
226
227
228/**
229 * Locates the extent covering the specified offset, or the one after it.
230 *
231 * @returns The closest extent. NULL if off is 0 and there are no extent
232 * covering byte 0 yet.
233 * @param pThis The memory file.
234 * @param off The offset (0-positive).
235 * @param pfHit Where to indicate whether the extent is a
236 * direct hit (@c true) or just a closest match
237 * (@c false).
238 */
239DECLINLINE(PRTVFSMEMEXTENT) rtVfsMemFile_LocateExtent(PRTVFSMEMFILE pThis, uint64_t off, bool *pfHit)
240{
241 /*
242 * The most likely case is that we're hitting the extent we used in the
243 * previous access or the one immediately following it.
244 */
245 PRTVFSMEMEXTENT pExtent = pThis->pCurExt;
246 if (!pExtent)
247 return rtVfsMemFile_LocateExtentSlow(pThis, off, pfHit);
248
249 if (off - pExtent->off >= pExtent->cb)
250 {
251 pExtent = RTListGetNext(&pThis->ExtentHead, pExtent, RTVFSMEMEXTENT, Entry);
252 if ( !pExtent
253 || off - pExtent->off >= pExtent->cb)
254 return rtVfsMemFile_LocateExtentSlow(pThis, off, pfHit);
255 pThis->pCurExt = pExtent;
256 }
257
258 *pfHit = true;
259 return pExtent;
260}
261
262
263/**
264 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnRead}
265 */
266static DECLCALLBACK(int) rtVfsMemFile_Read(void *pvThis, RTFOFF off, PRTSGBUF pSgBuf, bool fBlocking, size_t *pcbRead)
267{
268 PRTVFSMEMFILE pThis = (PRTVFSMEMFILE)pvThis;
269
270 Assert(pSgBuf->cSegs == 1);
271 NOREF(fBlocking);
272
273 /*
274 * Find the current position and check if it's within the file.
275 */
276 uint64_t offUnsigned = off < 0 ? pThis->offCurPos : (uint64_t)off;
277 if (offUnsigned >= (uint64_t)pThis->Base.ObjInfo.cbObject)
278 {
279 if (pcbRead)
280 {
281 *pcbRead = 0;
282 pThis->offCurPos = offUnsigned;
283 return VINF_EOF;
284 }
285 return VERR_EOF;
286 }
287
288 size_t cbLeftToRead;
289 if (offUnsigned + pSgBuf->paSegs[0].cbSeg > (uint64_t)pThis->Base.ObjInfo.cbObject)
290 {
291 if (!pcbRead)
292 return VERR_EOF;
293 *pcbRead = cbLeftToRead = (size_t)((uint64_t)pThis->Base.ObjInfo.cbObject - offUnsigned);
294 }
295 else
296 {
297 cbLeftToRead = pSgBuf->paSegs[0].cbSeg;
298 if (pcbRead)
299 *pcbRead = cbLeftToRead;
300 }
301
302 /*
303 * Ok, we've got a valid stretch within the file. Do the reading.
304 */
305 if (cbLeftToRead > 0)
306 {
307 RTSgBufAdvance(pSgBuf, cbLeftToRead);
308
309 uint8_t *pbDst = (uint8_t *)pSgBuf->paSegs[0].pvSeg;
310 bool fHit;
311 PRTVFSMEMEXTENT pExtent = rtVfsMemFile_LocateExtent(pThis, offUnsigned, &fHit);
312 for (;;)
313 {
314 size_t cbThisRead;
315
316 /*
317 * Do we hit an extent covering the current file surface?
318 */
319 if (fHit)
320 {
321 /* Yes, copy the data. */
322 Assert(offUnsigned - pExtent->off < pExtent->cb);
323 size_t const offExtent = (size_t)(offUnsigned - pExtent->off);
324 cbThisRead = pExtent->cb - offExtent;
325 if (cbThisRead >= cbLeftToRead)
326 cbThisRead = cbLeftToRead;
327
328 memcpy(pbDst, &pExtent->abData[offUnsigned - pExtent->off], cbThisRead);
329
330 offUnsigned += cbThisRead;
331 cbLeftToRead -= cbThisRead;
332 if (!cbLeftToRead)
333 break;
334 pbDst += cbThisRead;
335
336 /* Advance, looping immediately if not sparse. */
337 PRTVFSMEMEXTENT pNext = RTListGetNext(&pThis->ExtentHead, pExtent, RTVFSMEMEXTENT, Entry);
338 if ( pNext
339 && pNext->off == pExtent->off + pExtent->cb)
340 {
341 pExtent = pNext;
342 continue;
343 }
344
345 Assert(!pNext || pNext->off > pExtent->off);
346 pExtent = pNext;
347 fHit = false;
348 }
349 else
350 Assert(!pExtent || pExtent->off > offUnsigned);
351
352 /*
353 * No extent of this portion (sparse file) - Read zeros.
354 */
355 if ( !pExtent
356 || offUnsigned + cbLeftToRead <= pExtent->off)
357 cbThisRead = cbLeftToRead;
358 else
359 cbThisRead = (size_t)(pExtent->off - offUnsigned);
360
361 RT_BZERO(pbDst, cbThisRead);
362
363 offUnsigned += cbThisRead;
364 cbLeftToRead -= cbThisRead;
365 if (!cbLeftToRead)
366 break;
367 pbDst += cbThisRead;
368
369 /* Go on and read content from the next extent. */
370 fHit = true;
371 }
372 }
373
374 pThis->offCurPos = offUnsigned;
375 return VINF_SUCCESS;
376}
377
378
379/**
380 * Allocates a new extent covering the ground at @a offUnsigned.
381 *
382 * @returns Pointer to the new extent on success, NULL if we're out of memory.
383 * @param pThis The memory file.
384 * @param offUnsigned The location to allocate the extent at.
385 * @param cbToWrite The number of bytes we're interested in writing
386 * starting at @a offUnsigned.
387 * @param pNext The extention after @a offUnsigned. NULL if
388 * none, i.e. we're allocating space at the end of
389 * the file.
390 */
391static PRTVFSMEMEXTENT rtVfsMemFile_AllocExtent(PRTVFSMEMFILE pThis, uint64_t offUnsigned, size_t cbToWrite,
392 PRTVFSMEMEXTENT pNext)
393{
394 /*
395 * Adjust the extent size if we haven't reached the max size yet.
396 */
397 if (pThis->cbExtent != RTVFSMEM_MAX_EXTENT_SIZE)
398 {
399 if (cbToWrite >= RTVFSMEM_MAX_EXTENT_SIZE)
400 pThis->cbExtent = RTVFSMEM_MAX_EXTENT_SIZE;
401 else if (!RTListIsEmpty(&pThis->ExtentHead))
402 {
403 uint32_t cbNextExtent = pThis->cbExtent;
404 if (RT_IS_POWER_OF_TWO(cbNextExtent))
405 cbNextExtent *= 2;
406 else
407 {
408 /* Make it a power of two (seeRTVfsMemorizeIoStreamAsFile). */
409 cbNextExtent = _4K;
410 while (cbNextExtent < pThis->cbExtent)
411 cbNextExtent *= 2;
412 }
413 if (((pThis->Base.ObjInfo.cbAllocated + cbNextExtent) & (cbNextExtent - 1)) == 0)
414 pThis->cbExtent = cbNextExtent;
415 }
416 }
417
418 /*
419 * Figure out the size and position of the extent we're adding.
420 */
421 uint64_t offExtent = offUnsigned & ~(uint64_t)(pThis->cbExtent - 1);
422 uint32_t cbExtent = pThis->cbExtent;
423
424 PRTVFSMEMEXTENT pPrev = pNext
425 ? RTListGetPrev(&pThis->ExtentHead, pNext, RTVFSMEMEXTENT, Entry)
426 : RTListGetLast(&pThis->ExtentHead, RTVFSMEMEXTENT, Entry);
427 uint64_t const offPrev = pPrev ? pPrev->off + pPrev->cb : 0;
428 if (offExtent < offPrev)
429 offExtent = offPrev;
430
431 if (pNext)
432 {
433 uint64_t cbMaxExtent = pNext->off - offExtent;
434 if (cbMaxExtent < cbExtent)
435 cbExtent = (uint32_t)cbMaxExtent;
436 }
437
438 /*
439 * Allocate, initialize and insert the new extent.
440 */
441 PRTVFSMEMEXTENT pNew = (PRTVFSMEMEXTENT)RTMemAllocZ(RT_UOFFSETOF_DYN(RTVFSMEMEXTENT, abData[cbExtent]));
442 if (pNew)
443 {
444 pNew->off = offExtent;
445 pNew->cb = cbExtent;
446 if (pPrev)
447 RTListNodeInsertAfter(&pPrev->Entry, &pNew->Entry);
448 else
449 RTListPrepend(&pThis->ExtentHead, &pNew->Entry);
450
451 pThis->Base.ObjInfo.cbAllocated += cbExtent;
452 }
453 /** @todo retry with minimum size. */
454
455 return pNew;
456}
457
458
459/**
460 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnWrite}
461 */
462static DECLCALLBACK(int) rtVfsMemFile_Write(void *pvThis, RTFOFF off, PRTSGBUF pSgBuf, bool fBlocking, size_t *pcbWritten)
463{
464 PRTVFSMEMFILE pThis = (PRTVFSMEMFILE)pvThis;
465
466 Assert(pSgBuf->cSegs == 1);
467 NOREF(fBlocking);
468
469 /*
470 * Validate the write and set up the write loop.
471 */
472 size_t cbLeftToWrite = pSgBuf->paSegs[0].cbSeg;
473 if (!cbLeftToWrite)
474 return VINF_SUCCESS; /* pcbWritten is already 0. */
475 uint64_t offUnsigned = off < 0 ? pThis->offCurPos : (uint64_t)off;
476 if (offUnsigned + cbLeftToWrite >= (uint64_t)RTFOFF_MAX)
477 return VERR_OUT_OF_RANGE;
478
479 int rc = VINF_SUCCESS;
480 uint8_t const *pbSrc = (uint8_t const *)pSgBuf->paSegs[0].pvSeg;
481 bool fHit;
482 PRTVFSMEMEXTENT pExtent = rtVfsMemFile_LocateExtent(pThis, offUnsigned, &fHit);
483 for (;;)
484 {
485 /*
486 * If we didn't hit an extent, allocate one (unless it's all zeros).
487 */
488 if (!fHit)
489 {
490 Assert(!pExtent || pExtent->off > offUnsigned);
491
492 /* Skip leading zeros if there is a whole bunch of them. */
493 uint8_t const *pbSrcNZ = (uint8_t const *)ASMMemFirstNonZero(pbSrc, cbLeftToWrite);
494 size_t cbZeros = pbSrcNZ ? pbSrcNZ - pbSrc : cbLeftToWrite;
495 if (cbZeros)
496 {
497 uint64_t const cbToNext = pExtent ? pExtent->off - offUnsigned : UINT64_MAX;
498 if (cbZeros > cbToNext)
499 cbZeros = (size_t)cbToNext;
500 offUnsigned += cbZeros;
501 cbLeftToWrite -= cbZeros;
502 if (!cbLeftToWrite)
503 break;
504 pbSrc += cbZeros;
505
506 Assert(!pExtent || offUnsigned <= pExtent->off);
507 if (pExtent && pExtent->off == offUnsigned)
508 {
509 fHit = true;
510 continue;
511 }
512 }
513
514 fHit = true;
515 pExtent = rtVfsMemFile_AllocExtent(pThis, offUnsigned, cbLeftToWrite, pExtent);
516 if (!pExtent)
517 {
518 rc = VERR_NO_MEMORY;
519 break;
520 }
521 }
522 Assert(offUnsigned - pExtent->off < pExtent->cb);
523
524 /*
525 * Copy the source data into the current extent.
526 */
527 uint32_t const offDst = (uint32_t)(offUnsigned - pExtent->off);
528 uint32_t cbThisWrite = pExtent->cb - offDst;
529 if (cbThisWrite > cbLeftToWrite)
530 cbThisWrite = (uint32_t)cbLeftToWrite;
531 memcpy(&pExtent->abData[offDst], pbSrc, cbThisWrite);
532
533 offUnsigned += cbThisWrite;
534 cbLeftToWrite -= cbThisWrite;
535 if (!cbLeftToWrite)
536 break;
537 pbSrc += cbThisWrite;
538 Assert(offUnsigned == pExtent->off + pExtent->cb);
539
540 /*
541 * Advance to the next extent (emulate the lookup).
542 */
543 pExtent = RTListGetNext(&pThis->ExtentHead, pExtent, RTVFSMEMEXTENT, Entry);
544 fHit = pExtent && (offUnsigned - pExtent->off < pExtent->cb);
545 }
546
547 /*
548 * Update the state, set return value and return.
549 * Note! There must be no alternative exit path from the loop above.
550 */
551 pThis->offCurPos = offUnsigned;
552 if ((uint64_t)pThis->Base.ObjInfo.cbObject < offUnsigned)
553 pThis->Base.ObjInfo.cbObject = offUnsigned;
554
555 size_t const cbWritten = pSgBuf->paSegs[0].cbSeg - cbLeftToWrite;
556 if (pcbWritten)
557 *pcbWritten = cbWritten;
558 RTSgBufAdvance(pSgBuf, cbWritten);
559 return rc;
560}
561
562
563/**
564 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnFlush}
565 */
566static DECLCALLBACK(int) rtVfsMemFile_Flush(void *pvThis)
567{
568 NOREF(pvThis);
569 return VINF_SUCCESS;
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 within RTFOFF type 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 * @interface_method_impl{RTVFSFILEOPS,pfnSetSize}
705 */
706static DECLCALLBACK(int) rtVfsMemFile_SetSize(void *pvThis, uint64_t cbFile, uint32_t fFlags)
707{
708 AssertReturn(RTVFSFILE_SIZE_F_IS_VALID(fFlags), VERR_INVALID_PARAMETER);
709
710 PRTVFSMEMFILE pThis = (PRTVFSMEMFILE)pvThis;
711 if ( (fFlags & RTVFSFILE_SIZE_F_ACTION_MASK) == RTVFSFILE_SIZE_F_NORMAL
712 && (RTFOFF)cbFile >= pThis->Base.ObjInfo.cbObject)
713 {
714 /* Growing is just a matter of increasing the size of the object. */
715 pThis->Base.ObjInfo.cbObject = cbFile;
716 return VINF_SUCCESS;
717 }
718
719 AssertMsgFailed(("Lucky you! You get to implement this (or bug bird about it).\n"));
720 return VERR_NOT_IMPLEMENTED;
721}
722
723
724/**
725 * @interface_method_impl{RTVFSFILEOPS,pfnQueryMaxSize}
726 */
727static DECLCALLBACK(int) rtVfsMemFile_QueryMaxSize(void *pvThis, uint64_t *pcbMax)
728{
729 RT_NOREF(pvThis);
730 *pcbMax = ~(size_t)0 >> 1;
731 return VINF_SUCCESS;
732}
733
734
735/**
736 * Memory file operations.
737 */
738DECL_HIDDEN_CONST(const RTVFSFILEOPS) g_rtVfsMemFileOps =
739{
740 { /* Stream */
741 { /* Obj */
742 RTVFSOBJOPS_VERSION,
743 RTVFSOBJTYPE_FILE,
744 "MemFile",
745 rtVfsMemFile_Close,
746 rtVfsMemFile_QueryInfo,
747 NULL,
748 RTVFSOBJOPS_VERSION
749 },
750 RTVFSIOSTREAMOPS_VERSION,
751 RTVFSIOSTREAMOPS_FEAT_NO_SG,
752 rtVfsMemFile_Read,
753 rtVfsMemFile_Write,
754 rtVfsMemFile_Flush,
755 NULL /*PollOne*/,
756 rtVfsMemFile_Tell,
757 NULL /*Skip*/,
758 NULL /*ZeroFill*/,
759 RTVFSIOSTREAMOPS_VERSION,
760 },
761 RTVFSFILEOPS_VERSION,
762 /*RTVFSIOFILEOPS_FEAT_NO_AT_OFFSET*/ 0,
763 { /* ObjSet */
764 RTVFSOBJSETOPS_VERSION,
765 RT_UOFFSETOF(RTVFSFILEOPS, ObjSet) - RT_UOFFSETOF(RTVFSFILEOPS, Stream.Obj),
766 rtVfsMemFile_SetMode,
767 rtVfsMemFile_SetTimes,
768 rtVfsMemFile_SetOwner,
769 RTVFSOBJSETOPS_VERSION
770 },
771 rtVfsMemFile_Seek,
772 rtVfsMemFile_QuerySize,
773 rtVfsMemFile_SetSize,
774 rtVfsMemFile_QueryMaxSize,
775 RTVFSFILEOPS_VERSION
776};
777
778
779/**
780 * Initialize the RTVFSMEMFILE::Base.ObjInfo specific members.
781 *
782 * @param pObjInfo The object info to init.
783 * @param cbObject The object size set.
784 */
785static void rtVfsMemInitObjInfo(PRTFSOBJINFO pObjInfo, uint64_t cbObject)
786{
787 pObjInfo->cbObject = cbObject;
788 pObjInfo->cbAllocated = cbObject;
789 pObjInfo->Attr.fMode = RTFS_DOS_NT_NORMAL | RTFS_TYPE_FILE | RTFS_UNIX_IRWXU;
790 pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX;
791 pObjInfo->Attr.u.Unix.uid = NIL_RTUID;
792 pObjInfo->Attr.u.Unix.gid = NIL_RTGID;
793 pObjInfo->Attr.u.Unix.cHardlinks = 1;
794 pObjInfo->Attr.u.Unix.INodeIdDevice = 0;
795 pObjInfo->Attr.u.Unix.INodeId = 0;
796 pObjInfo->Attr.u.Unix.fFlags = 0;
797 pObjInfo->Attr.u.Unix.GenerationId = 0;
798 pObjInfo->Attr.u.Unix.Device = 0;
799 RTTimeNow(&pObjInfo->AccessTime);
800 pObjInfo->ModificationTime = pObjInfo->AccessTime;
801 pObjInfo->ChangeTime = pObjInfo->AccessTime;
802 pObjInfo->BirthTime = pObjInfo->AccessTime;
803}
804
805
806/**
807 * Initialize the RTVFSMEMFILE specific members.
808 *
809 * @param pThis The memory file to initialize.
810 * @param cbObject The object size for estimating extent size.
811 * @param fFlags The user specified flags.
812 */
813static void rtVfsMemFileInit(PRTVFSMEMFILE pThis, RTFOFF cbObject, uint32_t fFlags)
814{
815 pThis->offCurPos = 0;
816 pThis->pCurExt = NULL;
817 RTListInit(&pThis->ExtentHead);
818 if (cbObject <= 0)
819 pThis->cbExtent = _4K;
820 else if (cbObject < RTVFSMEM_MAX_EXTENT_SIZE)
821 pThis->cbExtent = fFlags & RTFILE_O_WRITE ? _4K : cbObject;
822 else
823 pThis->cbExtent = RTVFSMEM_MAX_EXTENT_SIZE;
824}
825
826
827/**
828 * Rewinds the file to position 0 and clears the WRITE flag if necessary.
829 *
830 * @param pThis The memory file instance.
831 * @param fFlags The user specified flags.
832 */
833static void rtVfsMemFileResetAndFixWriteFlag(PRTVFSMEMFILE pThis, uint32_t fFlags)
834{
835 pThis->pCurExt = RTListGetFirst(&pThis->ExtentHead, RTVFSMEMEXTENT, Entry);
836 pThis->offCurPos = 0;
837
838 if (!(fFlags & RTFILE_O_WRITE))
839 {
840 /** @todo clear RTFILE_O_WRITE from the resulting. */
841 }
842}
843
844
845RTDECL(int) RTVfsMemFileCreate(RTVFSIOSTREAM hVfsIos, size_t cbEstimate, PRTVFSFILE phVfsFile)
846{
847 /*
848 * Create a memory file instance and set the extension size according to the
849 * buffer size. Add the WRITE flag so we can use normal write APIs for
850 * copying the buffer.
851 */
852 RTVFSFILE hVfsFile;
853 PRTVFSMEMFILE pThis;
854 int rc = RTVfsNewFile(&g_rtVfsMemFileOps, sizeof(*pThis), RTFILE_O_READ | RTFILE_O_WRITE, NIL_RTVFS, NIL_RTVFSLOCK,
855 &hVfsFile, (void **)&pThis);
856 if (RT_SUCCESS(rc))
857 {
858 rtVfsMemInitObjInfo(&pThis->Base.ObjInfo, 0);
859 rtVfsMemFileInit(pThis, cbEstimate, RTFILE_O_READ | RTFILE_O_WRITE);
860
861 if (hVfsIos != NIL_RTVFSIOSTREAM)
862 {
863 RTVFSIOSTREAM hVfsIosDst = RTVfsFileToIoStream(hVfsFile);
864 rc = RTVfsUtilPumpIoStreams(hVfsIos, hVfsIosDst, pThis->cbExtent);
865 RTVfsIoStrmRelease(hVfsIosDst);
866 }
867
868 if (RT_SUCCESS(rc))
869 {
870 *phVfsFile = hVfsFile;
871 return VINF_SUCCESS;
872 }
873
874 RTVfsFileRelease(hVfsFile);
875 }
876 return rc;
877}
878
879
880RTDECL(int) RTVfsMemIoStrmCreate(RTVFSIOSTREAM hVfsIos, size_t cbEstimate, PRTVFSIOSTREAM phVfsIos)
881{
882 RTVFSFILE hVfsFile;
883 int rc = RTVfsMemFileCreate(hVfsIos, cbEstimate, &hVfsFile);
884 if (RT_SUCCESS(rc))
885 {
886 *phVfsIos = RTVfsFileToIoStream(hVfsFile);
887 AssertStmt(*phVfsIos != NIL_RTVFSIOSTREAM, rc = VERR_INTERNAL_ERROR_2);
888 RTVfsFileRelease(hVfsFile);
889 }
890 return rc;
891}
892
893
894RTDECL(int) RTVfsFileFromBuffer(uint32_t fFlags, void const *pvBuf, size_t cbBuf, PRTVFSFILE phVfsFile)
895{
896 /*
897 * Create a memory file instance and set the extension size according to the
898 * buffer size. Add the WRITE flag so we can use normal write APIs for
899 * copying the buffer.
900 */
901 RTVFSFILE hVfsFile;
902 PRTVFSMEMFILE pThis;
903 int rc = RTVfsNewFile(&g_rtVfsMemFileOps, sizeof(*pThis), fFlags | RTFILE_O_WRITE, NIL_RTVFS, NIL_RTVFSLOCK,
904 &hVfsFile, (void **)&pThis);
905 if (RT_SUCCESS(rc))
906 {
907 rtVfsMemInitObjInfo(&pThis->Base.ObjInfo, cbBuf);
908 rtVfsMemFileInit(pThis, cbBuf, fFlags);
909
910 /*
911 * Copy the buffer and reposition the file pointer to the start.
912 */
913 rc = RTVfsFileWrite(hVfsFile, pvBuf, cbBuf, NULL);
914 if (RT_SUCCESS(rc))
915 {
916 rtVfsMemFileResetAndFixWriteFlag(pThis, fFlags);
917 *phVfsFile = hVfsFile;
918 return VINF_SUCCESS;
919 }
920 RTVfsFileRelease(hVfsFile);
921 }
922 return rc;
923}
924
925
926RTDECL(int) RTVfsIoStrmFromBuffer(uint32_t fFlags, void const *pvBuf, size_t cbBuf, PRTVFSIOSTREAM phVfsIos)
927{
928 RTVFSFILE hVfsFile;
929 int rc = RTVfsFileFromBuffer(fFlags, pvBuf, cbBuf, &hVfsFile);
930 if (RT_SUCCESS(rc))
931 {
932 *phVfsIos = RTVfsFileToIoStream(hVfsFile);
933 RTVfsFileRelease(hVfsFile);
934 }
935 return rc;
936}
937
938
939RTDECL(int) RTVfsMemorizeIoStreamAsFile(RTVFSIOSTREAM hVfsIos, uint32_t fFlags, PRTVFSFILE phVfsFile)
940{
941 /*
942 * Create a memory file instance and try set the extension size to match
943 * the length of the I/O stream.
944 */
945 RTFSOBJINFO ObjInfo;
946 int rc = RTVfsIoStrmQueryInfo(hVfsIos, &ObjInfo, RTFSOBJATTRADD_UNIX);
947 if (RT_SUCCESS(rc))
948 {
949 RTVFSFILE hVfsFile;
950 PRTVFSMEMFILE pThis;
951 rc = RTVfsNewFile(&g_rtVfsMemFileOps, sizeof(*pThis), fFlags | RTFILE_O_WRITE, NIL_RTVFS, NIL_RTVFSLOCK,
952 &hVfsFile, (void **)&pThis);
953 if (RT_SUCCESS(rc))
954 {
955 pThis->Base.ObjInfo = ObjInfo;
956 rtVfsMemFileInit(pThis, ObjInfo.cbObject, fFlags);
957
958 /*
959 * Copy the stream.
960 */
961 RTVFSIOSTREAM hVfsIosDst = RTVfsFileToIoStream(hVfsFile);
962 rc = RTVfsUtilPumpIoStreams(hVfsIos, hVfsIosDst, pThis->cbExtent);
963 RTVfsIoStrmRelease(hVfsIosDst);
964 if (RT_SUCCESS(rc))
965 {
966 rtVfsMemFileResetAndFixWriteFlag(pThis, fFlags);
967 *phVfsFile = hVfsFile;
968 return VINF_SUCCESS;
969 }
970 RTVfsFileRelease(hVfsFile);
971 }
972 }
973 return rc;
974}
975
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use