VirtualBox

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

Last change on this file since 97885 was 96407, checked in by vboxsync, 2 years ago

scm copyright and license note update

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

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