VirtualBox

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

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

Runtime/vfs/vfsmemory.cpp: Fix a bug causing the extent lookup to fail if the file starts with zeroes and the offset for the lookup falls into that area, fix a second bug in the write code where the zeros in the source buffer are not skipped leading to data corruption

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