VirtualBox

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

Last change on this file since 74525 was 73097, checked in by vboxsync, 6 years ago

*: Made RT_UOFFSETOF, RT_OFFSETOF, RT_UOFFSETOF_ADD and RT_OFFSETOF_ADD work like builtin_offsetof() and require compile time resolvable requests, adding RT_UOFFSETOF_DYN for the dynamic questions that can only be answered at runtime.

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