VirtualBox

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

Last change on this file since 45234 was 40292, checked in by vboxsync, 13 years ago

RTVfsMemFile bugfix: rtVfsMemFile_Write regression from previous fix.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 23.6 KB
Line 
1/* $Id: vfsmemory.cpp 40292 2012-02-29 11:29:35Z vboxsync $ */
2/** @file
3 * IPRT - Virtual File System, Memory Backed VFS.
4 */
5
6/*
7 * Copyright (C) 2010-2012 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 || pExtent->off < 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 Assert(off < 0);
256 NOREF(fBlocking);
257
258 /*
259 * Find the current position and check if it's within the file.
260 */
261 uint64_t offUnsigned = off < 0 ? pThis->offCurPos : (uint64_t)off;
262 if (offUnsigned >= (uint64_t)pThis->Base.ObjInfo.cbObject)
263 {
264 if (pcbRead)
265 {
266 *pcbRead = 0;
267 pThis->offCurPos = offUnsigned;
268 return VINF_EOF;
269 }
270 return VERR_EOF;
271 }
272
273 size_t cbLeftToRead;
274 if (offUnsigned + pSgBuf->paSegs[0].cbSeg > (uint64_t)pThis->Base.ObjInfo.cbObject)
275 {
276 if (!pcbRead)
277 return VERR_EOF;
278 *pcbRead = cbLeftToRead = (size_t)((uint64_t)pThis->Base.ObjInfo.cbObject - offUnsigned);
279 }
280 else
281 {
282 cbLeftToRead = pSgBuf->paSegs[0].cbSeg;
283 if (pcbRead)
284 *pcbRead = cbLeftToRead;
285 }
286
287 /*
288 * Ok, we've got a valid stretch within the file. Do the reading.
289 */
290 if (cbLeftToRead > 0)
291 {
292 uint8_t *pbDst = (uint8_t *)pSgBuf->paSegs[0].pvSeg;
293 bool fHit;
294 PRTVFSMEMEXTENT pExtent = rtVfsMemFile_LocateExtent(pThis, offUnsigned, &fHit);
295 for (;;)
296 {
297 size_t cbThisRead;
298
299 /*
300 * Do we hit an extent covering the current file surface?
301 */
302 if (fHit)
303 {
304 /* Yes, copy the data. */
305 Assert(offUnsigned - pExtent->off < pExtent->cb);
306 size_t const offExtent = (size_t)(offUnsigned - pExtent->off);
307 cbThisRead = pExtent->cb - offExtent;
308 if (cbThisRead >= cbLeftToRead)
309 cbThisRead = cbLeftToRead;
310
311 memcpy(pbDst, &pExtent->abData[offUnsigned - pExtent->off], cbThisRead);
312
313 offUnsigned += cbThisRead;
314 cbLeftToRead -= cbThisRead;
315 if (!cbLeftToRead)
316 break;
317 pbDst += cbThisRead;
318
319 /* Advance, looping immediately if not sparse. */
320 PRTVFSMEMEXTENT pNext = RTListGetNext(&pThis->ExtentHead, pExtent, RTVFSMEMEXTENT, Entry);
321 if ( pNext
322 && pNext->off == pExtent->off + pExtent->cb)
323 {
324 pExtent = pNext;
325 continue;
326 }
327
328 Assert(!pNext || pNext->off > pExtent->off);
329 pExtent = pNext;
330 fHit = false;
331 }
332 else
333 Assert(!pExtent || pExtent->off > offUnsigned);
334
335 /*
336 * No extent of this portion (sparse file) - Read zeros.
337 */
338 if ( !pExtent
339 || offUnsigned + cbLeftToRead <= pExtent->off)
340 cbThisRead = cbLeftToRead;
341 else
342 cbThisRead = (size_t)(pExtent->off - offUnsigned);
343
344 RT_BZERO(pbDst, cbThisRead);
345
346 offUnsigned += cbThisRead;
347 cbLeftToRead -= cbThisRead;
348 if (!cbLeftToRead)
349 break;
350 pbDst += cbThisRead;
351
352 /* Go on and read content from the next extent. */
353 fHit = true;
354 }
355 }
356
357 pThis->offCurPos = offUnsigned;
358 return VINF_SUCCESS;
359}
360
361
362/**
363 * Allocates a new extent covering the ground at @a offUnsigned.
364 *
365 * @returns Pointer to the new extent on success, NULL if we're out of memory.
366 * @param pThis The memory file.
367 * @param offUnsigned The location to allocate the extent at.
368 * @param cbToWrite The number of bytes we're interested in writing
369 * starting at @a offUnsigned.
370 * @param pNext The extention after @a offUnsigned. NULL if
371 * none, i.e. we're allocating space at the end of
372 * the file.
373 */
374static PRTVFSMEMEXTENT rtVfsMemFile_AllocExtent(PRTVFSMEMFILE pThis, uint64_t offUnsigned, size_t cbToWrite,
375 PRTVFSMEMEXTENT pNext)
376{
377 /*
378 * Adjust the extent size if we haven't reached the max size yet.
379 */
380 if (pThis->cbExtent != RTVFSMEM_MAX_EXTENT_SIZE)
381 {
382 if (cbToWrite >= RTVFSMEM_MAX_EXTENT_SIZE)
383 pThis->cbExtent = RTVFSMEM_MAX_EXTENT_SIZE;
384 else if (!RTListIsEmpty(&pThis->ExtentHead))
385 {
386 uint32_t cbNextExtent = pThis->cbExtent;
387 if (RT_IS_POWER_OF_TWO(cbNextExtent))
388 cbNextExtent *= 2;
389 else
390 {
391 /* Make it a power of two (seeRTVfsMemorizeIoStreamAsFile). */
392 cbNextExtent = _4K;
393 while (cbNextExtent < pThis->cbExtent)
394 cbNextExtent *= 2;
395 }
396 if (((pThis->Base.ObjInfo.cbAllocated + cbNextExtent) & (cbNextExtent - 1)) == 0)
397 pThis->cbExtent = cbNextExtent;
398 }
399 }
400
401 /*
402 * Figure out the size and position of the extent we're adding.
403 */
404 uint64_t offExtent = offUnsigned & ~(uint64_t)(pThis->cbExtent - 1);
405 uint32_t cbExtent = pThis->cbExtent;
406
407 PRTVFSMEMEXTENT pPrev = pNext
408 ? RTListGetPrev(&pThis->ExtentHead, pNext, RTVFSMEMEXTENT, Entry)
409 : RTListGetLast(&pThis->ExtentHead, RTVFSMEMEXTENT, Entry);
410 uint64_t const offPrev = pPrev ? pPrev->off + pPrev->cb : 0;
411 if (offExtent < offPrev)
412 offExtent = offPrev;
413
414 if (pNext)
415 {
416 uint64_t cbMaxExtent = pNext->off - offExtent;
417 if (cbMaxExtent < cbExtent)
418 cbExtent = (uint32_t)cbMaxExtent;
419 }
420
421 /*
422 * Allocate, initialize and insert the new extent.
423 */
424 PRTVFSMEMEXTENT pNew = (PRTVFSMEMEXTENT)RTMemAllocZ(RT_OFFSETOF(RTVFSMEMEXTENT, abData[cbExtent]));
425 if (pNew)
426 {
427 pNew->off = offExtent;
428 pNew->cb = cbExtent;
429 if (pPrev)
430 RTListNodeInsertAfter(&pPrev->Entry, &pNew->Entry);
431 else
432 RTListPrepend(&pThis->ExtentHead, &pNew->Entry);
433
434 pThis->Base.ObjInfo.cbAllocated += cbExtent;
435 }
436 /** @todo retry with minimum size. */
437
438 return pNew;
439}
440
441
442/**
443 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnWrite}
444 */
445static DECLCALLBACK(int) rtVfsMemFile_Write(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbWritten)
446{
447 PRTVFSMEMFILE pThis = (PRTVFSMEMFILE)pvThis;
448
449 Assert(pSgBuf->cSegs == 1);
450 Assert(off < 0);
451 NOREF(fBlocking);
452
453 /*
454 * Validate the write and set up the write loop.
455 */
456 size_t cbLeftToWrite = pSgBuf->paSegs[0].cbSeg;
457 if (!cbLeftToWrite)
458 return VINF_SUCCESS; /* pcbWritten is already 0. */
459 uint64_t offUnsigned = off < 0 ? pThis->offCurPos : (uint64_t)off;
460 if (offUnsigned + cbLeftToWrite >= (uint64_t)RTFOFF_MAX)
461 return VERR_OUT_OF_RANGE;
462
463 int rc = VINF_SUCCESS;
464 uint8_t const *pbSrc = (uint8_t const *)pSgBuf->paSegs[0].pvSeg;
465 bool fHit;
466 PRTVFSMEMEXTENT pExtent = rtVfsMemFile_LocateExtent(pThis, offUnsigned, &fHit);
467 for (;;)
468 {
469 /*
470 * If we didn't hit an extent, allocate one (unless it's all zeros).
471 */
472 if (!fHit)
473 {
474 Assert(!pExtent || pExtent->off > offUnsigned);
475
476 /* Skip leading zeros if there is a whole bunch of them. */
477 uint8_t const *pbSrcNZ = (uint8_t const *)ASMMemIsAll8(pbSrc, cbLeftToWrite, 0);
478 size_t cbZeros = pbSrcNZ ? pbSrcNZ - pbSrc : cbLeftToWrite;
479 if (cbZeros)
480 {
481 uint64_t const cbToNext = pExtent ? pExtent->off - offUnsigned : UINT64_MAX;
482 if (cbZeros > cbToNext)
483 cbZeros = (size_t)cbToNext;
484 offUnsigned += cbZeros;
485 cbLeftToWrite -= cbZeros;
486 if (!cbLeftToWrite)
487 break;
488
489 Assert(!pExtent || offUnsigned <= pExtent->off);
490 if (pExtent && pExtent->off == offUnsigned)
491 {
492 fHit = true;
493 continue;
494 }
495 }
496
497 fHit = true;
498 pExtent = rtVfsMemFile_AllocExtent(pThis, offUnsigned, cbLeftToWrite, pExtent);
499 if (!pExtent)
500 {
501 rc = VERR_NO_MEMORY;
502 break;
503 }
504 }
505 Assert(offUnsigned - pExtent->off < pExtent->cb);
506
507 /*
508 * Copy the source data into the current extent.
509 */
510 uint32_t const offDst = (uint32_t)(offUnsigned - pExtent->off);
511 uint32_t cbThisWrite = pExtent->cb - offDst;
512 if (cbThisWrite > cbLeftToWrite)
513 cbThisWrite = (uint32_t)cbLeftToWrite;
514 memcpy(&pExtent->abData[offDst], pbSrc, cbThisWrite);
515
516 offUnsigned += cbLeftToWrite;
517 cbLeftToWrite -= cbThisWrite;
518 if (!cbLeftToWrite)
519 break;
520 pbSrc += cbThisWrite;
521 Assert(offUnsigned == pExtent->off + pExtent->cb);
522
523 /*
524 * Advance to the next extent (emulate the lookup).
525 */
526 pExtent = RTListGetNext(&pThis->ExtentHead, pExtent, RTVFSMEMEXTENT, Entry);
527 fHit = pExtent && (offUnsigned - pExtent->off < pExtent->cb);
528 }
529
530 /*
531 * Update the state, set return value and return.
532 * Note! There must be no alternative exit path from the loop above.
533 */
534 pThis->offCurPos = offUnsigned;
535 if ((uint64_t)pThis->Base.ObjInfo.cbObject < offUnsigned)
536 pThis->Base.ObjInfo.cbObject = offUnsigned;
537
538 if (pcbWritten)
539 *pcbWritten = pSgBuf->paSegs[0].cbSeg - cbLeftToWrite;
540 return rc;
541}
542
543
544/**
545 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnFlush}
546 */
547static DECLCALLBACK(int) rtVfsMemFile_Flush(void *pvThis)
548{
549 NOREF(pvThis);
550 return VINF_SUCCESS;
551}
552
553
554/**
555 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnPollOne}
556 */
557static DECLCALLBACK(int) rtVfsMemFile_PollOne(void *pvThis, uint32_t fEvents, RTMSINTERVAL cMillies, bool fIntr,
558 uint32_t *pfRetEvents)
559{
560 NOREF(pvThis);
561 int rc;
562 if (fEvents != RTPOLL_EVT_ERROR)
563 {
564 *pfRetEvents = fEvents & ~RTPOLL_EVT_ERROR;
565 rc = VINF_SUCCESS;
566 }
567 else
568 rc = RTVfsUtilDummyPollOne(fEvents, cMillies, fIntr, pfRetEvents);
569 return rc;
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 without 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 * Standard file operations.
705 */
706DECL_HIDDEN_CONST(const RTVFSFILEOPS) g_rtVfsStdFileOps =
707{
708 { /* Stream */
709 { /* Obj */
710 RTVFSOBJOPS_VERSION,
711 RTVFSOBJTYPE_FILE,
712 "MemFile",
713 rtVfsMemFile_Close,
714 rtVfsMemFile_QueryInfo,
715 RTVFSOBJOPS_VERSION
716 },
717 RTVFSIOSTREAMOPS_VERSION,
718 RTVFSIOSTREAMOPS_FEAT_NO_SG,
719 rtVfsMemFile_Read,
720 rtVfsMemFile_Write,
721 rtVfsMemFile_Flush,
722 rtVfsMemFile_PollOne,
723 rtVfsMemFile_Tell,
724 NULL /*Skip*/,
725 NULL /*ZeroFill*/,
726 RTVFSIOSTREAMOPS_VERSION,
727 },
728 RTVFSFILEOPS_VERSION,
729 /*RTVFSIOFILEOPS_FEAT_NO_AT_OFFSET*/ 0,
730 { /* ObjSet */
731 RTVFSOBJSETOPS_VERSION,
732 RT_OFFSETOF(RTVFSFILEOPS, Stream.Obj) - RT_OFFSETOF(RTVFSFILEOPS, ObjSet),
733 rtVfsMemFile_SetMode,
734 rtVfsMemFile_SetTimes,
735 rtVfsMemFile_SetOwner,
736 RTVFSOBJSETOPS_VERSION
737 },
738 rtVfsMemFile_Seek,
739 rtVfsMemFile_QuerySize,
740 RTVFSFILEOPS_VERSION
741};
742
743
744
745
746
747RTDECL(int) RTVfsMemorizeIoStreamAsFile(RTVFSIOSTREAM hVfsIos, uint32_t fFlags, PRTVFSFILE phVfsFile)
748{
749 /*
750 * Create a memory file instance and try set the extension size to match
751 * the length of the I/O stream.
752 */
753 RTFSOBJINFO ObjInfo;
754 int rc = RTVfsIoStrmQueryInfo(hVfsIos, &ObjInfo, RTFSOBJATTRADD_UNIX);
755 if (RT_SUCCESS(rc))
756 {
757 RTVFSFILE hVfsFile;
758 PRTVFSMEMFILE pThis;
759 rc = RTVfsNewFile(&g_rtVfsStdFileOps, sizeof(*pThis), fFlags | RTFILE_O_WRITE, NIL_RTVFS, NIL_RTVFSLOCK,
760 &hVfsFile, (void **)&pThis);
761 if (RT_SUCCESS(rc))
762 {
763 pThis->Base.ObjInfo = ObjInfo;
764 pThis->offCurPos = 0;
765 pThis->pCurExt = NULL;
766 RTListInit(&pThis->ExtentHead);
767 if (ObjInfo.cbObject <= 0)
768 pThis->cbExtent = _4K;
769 else if (ObjInfo.cbObject < RTVFSMEM_MAX_EXTENT_SIZE)
770 pThis->cbExtent = _4K /* ObjInfo.cbObject */;
771 else
772 pThis->cbExtent = RTVFSMEM_MAX_EXTENT_SIZE;
773
774 /*
775 * Copy the stream.
776 */
777 RTVFSIOSTREAM hVfsIosDst = RTVfsFileToIoStream(hVfsFile);
778 rc = RTVfsUtilPumpIoStreams(hVfsIos, hVfsIosDst, pThis->cbExtent);
779 RTVfsIoStrmRelease(hVfsIosDst);
780 if (RT_SUCCESS(rc))
781 {
782 pThis->pCurExt = RTListGetFirst(&pThis->ExtentHead, RTVFSMEMEXTENT, Entry);
783 pThis->offCurPos = 0;
784
785 if (!(fFlags & RTFILE_O_WRITE))
786 {
787 /** @todo clear RTFILE_O_WRITE from the resulting. */
788 }
789 *phVfsFile = hVfsFile;
790 return VINF_SUCCESS;
791 }
792 RTVfsFileRelease(hVfsFile);
793 }
794 }
795 return rc;
796}
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