VirtualBox

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

Last change on this file since 105100 was 105100, checked in by vboxsync, 5 months ago

Runtime/vfsmemory.cpp: Implement shrinking the memory file (required for TPM NVRAM), bugref:10701

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 30.7 KB
Line 
1/* $Id: vfsmemory.cpp 105100 2024-07-02 13:10:46Z 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 {
713 if ((RTFOFF)cbFile < pThis->Base.ObjInfo.cbObject)
714 {
715 /* Remove any extent beyond the file size. */
716 bool fHit;
717 PRTVFSMEMEXTENT pExtent = rtVfsMemFile_LocateExtent(pThis, cbFile, &fHit);
718 if ( fHit
719 && cbFile < (pExtent->off + pExtent->cb))
720 {
721 /* Clear the data in this extent. */
722 uint64_t cbRemaining = cbFile - pExtent->off;
723 memset(&pExtent->abData[cbRemaining], 0, pExtent->cb - cbRemaining);
724 pExtent = RTListGetNext(&pThis->ExtentHead, pExtent, RTVFSMEMEXTENT, Entry);
725 }
726
727 while (pExtent)
728 {
729 PRTVFSMEMEXTENT pFree = pExtent;
730 pExtent = RTListGetNext(&pThis->ExtentHead, pExtent, RTVFSMEMEXTENT, Entry);
731
732 RTListNodeRemove(&pFree->Entry);
733 RTMemFree(pFree);
734 }
735
736 pThis->pCurExt = NULL;
737 }
738 /* else: Growing is just a matter of increasing the size of the object. */
739
740 pThis->Base.ObjInfo.cbObject = cbFile;
741 return VINF_SUCCESS;
742 }
743
744 AssertMsgFailed(("Lucky you! You get to implement this (or bug bird about it).\n"));
745 return VERR_NOT_IMPLEMENTED;
746}
747
748
749/**
750 * @interface_method_impl{RTVFSFILEOPS,pfnQueryMaxSize}
751 */
752static DECLCALLBACK(int) rtVfsMemFile_QueryMaxSize(void *pvThis, uint64_t *pcbMax)
753{
754 RT_NOREF(pvThis);
755 *pcbMax = ~(size_t)0 >> 1;
756 return VINF_SUCCESS;
757}
758
759
760/**
761 * Memory file operations.
762 */
763DECL_HIDDEN_CONST(const RTVFSFILEOPS) g_rtVfsMemFileOps =
764{
765 { /* Stream */
766 { /* Obj */
767 RTVFSOBJOPS_VERSION,
768 RTVFSOBJTYPE_FILE,
769 "MemFile",
770 rtVfsMemFile_Close,
771 rtVfsMemFile_QueryInfo,
772 NULL,
773 RTVFSOBJOPS_VERSION
774 },
775 RTVFSIOSTREAMOPS_VERSION,
776 RTVFSIOSTREAMOPS_FEAT_NO_SG,
777 rtVfsMemFile_Read,
778 rtVfsMemFile_Write,
779 rtVfsMemFile_Flush,
780 NULL /*PollOne*/,
781 rtVfsMemFile_Tell,
782 NULL /*Skip*/,
783 NULL /*ZeroFill*/,
784 RTVFSIOSTREAMOPS_VERSION,
785 },
786 RTVFSFILEOPS_VERSION,
787 /*RTVFSIOFILEOPS_FEAT_NO_AT_OFFSET*/ 0,
788 { /* ObjSet */
789 RTVFSOBJSETOPS_VERSION,
790 RT_UOFFSETOF(RTVFSFILEOPS, ObjSet) - RT_UOFFSETOF(RTVFSFILEOPS, Stream.Obj),
791 rtVfsMemFile_SetMode,
792 rtVfsMemFile_SetTimes,
793 rtVfsMemFile_SetOwner,
794 RTVFSOBJSETOPS_VERSION
795 },
796 rtVfsMemFile_Seek,
797 rtVfsMemFile_QuerySize,
798 rtVfsMemFile_SetSize,
799 rtVfsMemFile_QueryMaxSize,
800 RTVFSFILEOPS_VERSION
801};
802
803
804/**
805 * Initialize the RTVFSMEMFILE::Base.ObjInfo specific members.
806 *
807 * @param pObjInfo The object info to init.
808 * @param cbObject The object size set.
809 */
810static void rtVfsMemInitObjInfo(PRTFSOBJINFO pObjInfo, uint64_t cbObject)
811{
812 pObjInfo->cbObject = cbObject;
813 pObjInfo->cbAllocated = cbObject;
814 pObjInfo->Attr.fMode = RTFS_DOS_NT_NORMAL | RTFS_TYPE_FILE | RTFS_UNIX_IRWXU;
815 pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX;
816 pObjInfo->Attr.u.Unix.uid = NIL_RTUID;
817 pObjInfo->Attr.u.Unix.gid = NIL_RTGID;
818 pObjInfo->Attr.u.Unix.cHardlinks = 1;
819 pObjInfo->Attr.u.Unix.INodeIdDevice = 0;
820 pObjInfo->Attr.u.Unix.INodeId = 0;
821 pObjInfo->Attr.u.Unix.fFlags = 0;
822 pObjInfo->Attr.u.Unix.GenerationId = 0;
823 pObjInfo->Attr.u.Unix.Device = 0;
824 RTTimeNow(&pObjInfo->AccessTime);
825 pObjInfo->ModificationTime = pObjInfo->AccessTime;
826 pObjInfo->ChangeTime = pObjInfo->AccessTime;
827 pObjInfo->BirthTime = pObjInfo->AccessTime;
828}
829
830
831/**
832 * Initialize the RTVFSMEMFILE specific members.
833 *
834 * @param pThis The memory file to initialize.
835 * @param cbObject The object size for estimating extent size.
836 * @param fFlags The user specified flags.
837 */
838static void rtVfsMemFileInit(PRTVFSMEMFILE pThis, RTFOFF cbObject, uint32_t fFlags)
839{
840 pThis->offCurPos = 0;
841 pThis->pCurExt = NULL;
842 RTListInit(&pThis->ExtentHead);
843 if (cbObject <= 0)
844 pThis->cbExtent = _4K;
845 else if (cbObject < RTVFSMEM_MAX_EXTENT_SIZE)
846 pThis->cbExtent = fFlags & RTFILE_O_WRITE ? _4K : cbObject;
847 else
848 pThis->cbExtent = RTVFSMEM_MAX_EXTENT_SIZE;
849}
850
851
852/**
853 * Rewinds the file to position 0 and clears the WRITE flag if necessary.
854 *
855 * @param pThis The memory file instance.
856 * @param fFlags The user specified flags.
857 */
858static void rtVfsMemFileResetAndFixWriteFlag(PRTVFSMEMFILE pThis, uint32_t fFlags)
859{
860 pThis->pCurExt = RTListGetFirst(&pThis->ExtentHead, RTVFSMEMEXTENT, Entry);
861 pThis->offCurPos = 0;
862
863 if (!(fFlags & RTFILE_O_WRITE))
864 {
865 /** @todo clear RTFILE_O_WRITE from the resulting. */
866 }
867}
868
869
870RTDECL(int) RTVfsMemFileCreate(RTVFSIOSTREAM hVfsIos, size_t cbEstimate, PRTVFSFILE phVfsFile)
871{
872 /*
873 * Create a memory file instance and set the extension size according to the
874 * buffer size. Add the WRITE flag so we can use normal write APIs for
875 * copying the buffer.
876 */
877 RTVFSFILE hVfsFile;
878 PRTVFSMEMFILE pThis;
879 int rc = RTVfsNewFile(&g_rtVfsMemFileOps, sizeof(*pThis), RTFILE_O_READ | RTFILE_O_WRITE, NIL_RTVFS, NIL_RTVFSLOCK,
880 &hVfsFile, (void **)&pThis);
881 if (RT_SUCCESS(rc))
882 {
883 rtVfsMemInitObjInfo(&pThis->Base.ObjInfo, 0);
884 rtVfsMemFileInit(pThis, cbEstimate, RTFILE_O_READ | RTFILE_O_WRITE);
885
886 if (hVfsIos != NIL_RTVFSIOSTREAM)
887 {
888 RTVFSIOSTREAM hVfsIosDst = RTVfsFileToIoStream(hVfsFile);
889 rc = RTVfsUtilPumpIoStreams(hVfsIos, hVfsIosDst, pThis->cbExtent);
890 RTVfsIoStrmRelease(hVfsIosDst);
891 }
892
893 if (RT_SUCCESS(rc))
894 {
895 *phVfsFile = hVfsFile;
896 return VINF_SUCCESS;
897 }
898
899 RTVfsFileRelease(hVfsFile);
900 }
901 return rc;
902}
903
904
905RTDECL(int) RTVfsMemIoStrmCreate(RTVFSIOSTREAM hVfsIos, size_t cbEstimate, PRTVFSIOSTREAM phVfsIos)
906{
907 RTVFSFILE hVfsFile;
908 int rc = RTVfsMemFileCreate(hVfsIos, cbEstimate, &hVfsFile);
909 if (RT_SUCCESS(rc))
910 {
911 *phVfsIos = RTVfsFileToIoStream(hVfsFile);
912 AssertStmt(*phVfsIos != NIL_RTVFSIOSTREAM, rc = VERR_INTERNAL_ERROR_2);
913 RTVfsFileRelease(hVfsFile);
914 }
915 return rc;
916}
917
918
919RTDECL(int) RTVfsFileFromBuffer(uint32_t fFlags, void const *pvBuf, size_t cbBuf, PRTVFSFILE phVfsFile)
920{
921 /*
922 * Create a memory file instance and set the extension size according to the
923 * buffer size. Add the WRITE flag so we can use normal write APIs for
924 * copying the buffer.
925 */
926 RTVFSFILE hVfsFile;
927 PRTVFSMEMFILE pThis;
928 int rc = RTVfsNewFile(&g_rtVfsMemFileOps, sizeof(*pThis), fFlags | RTFILE_O_WRITE, NIL_RTVFS, NIL_RTVFSLOCK,
929 &hVfsFile, (void **)&pThis);
930 if (RT_SUCCESS(rc))
931 {
932 rtVfsMemInitObjInfo(&pThis->Base.ObjInfo, cbBuf);
933 rtVfsMemFileInit(pThis, cbBuf, fFlags);
934
935 /*
936 * Copy the buffer and reposition the file pointer to the start.
937 */
938 rc = RTVfsFileWrite(hVfsFile, pvBuf, cbBuf, NULL);
939 if (RT_SUCCESS(rc))
940 {
941 rtVfsMemFileResetAndFixWriteFlag(pThis, fFlags);
942 *phVfsFile = hVfsFile;
943 return VINF_SUCCESS;
944 }
945 RTVfsFileRelease(hVfsFile);
946 }
947 return rc;
948}
949
950
951RTDECL(int) RTVfsIoStrmFromBuffer(uint32_t fFlags, void const *pvBuf, size_t cbBuf, PRTVFSIOSTREAM phVfsIos)
952{
953 RTVFSFILE hVfsFile;
954 int rc = RTVfsFileFromBuffer(fFlags, pvBuf, cbBuf, &hVfsFile);
955 if (RT_SUCCESS(rc))
956 {
957 *phVfsIos = RTVfsFileToIoStream(hVfsFile);
958 RTVfsFileRelease(hVfsFile);
959 }
960 return rc;
961}
962
963
964RTDECL(int) RTVfsMemorizeIoStreamAsFile(RTVFSIOSTREAM hVfsIos, uint32_t fFlags, PRTVFSFILE phVfsFile)
965{
966 /*
967 * Create a memory file instance and try set the extension size to match
968 * the length of the I/O stream.
969 */
970 RTFSOBJINFO ObjInfo;
971 int rc = RTVfsIoStrmQueryInfo(hVfsIos, &ObjInfo, RTFSOBJATTRADD_UNIX);
972 if (RT_SUCCESS(rc))
973 {
974 RTVFSFILE hVfsFile;
975 PRTVFSMEMFILE pThis;
976 rc = RTVfsNewFile(&g_rtVfsMemFileOps, sizeof(*pThis), fFlags | RTFILE_O_WRITE, NIL_RTVFS, NIL_RTVFSLOCK,
977 &hVfsFile, (void **)&pThis);
978 if (RT_SUCCESS(rc))
979 {
980 pThis->Base.ObjInfo = ObjInfo;
981 rtVfsMemFileInit(pThis, ObjInfo.cbObject, fFlags);
982
983 /*
984 * Copy the stream.
985 */
986 RTVFSIOSTREAM hVfsIosDst = RTVfsFileToIoStream(hVfsFile);
987 rc = RTVfsUtilPumpIoStreams(hVfsIos, hVfsIosDst, pThis->cbExtent);
988 RTVfsIoStrmRelease(hVfsIosDst);
989 if (RT_SUCCESS(rc))
990 {
991 rtVfsMemFileResetAndFixWriteFlag(pThis, fFlags);
992 *phVfsFile = hVfsFile;
993 return VINF_SUCCESS;
994 }
995 RTVfsFileRelease(hVfsFile);
996 }
997 }
998 return rc;
999}
1000
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