VirtualBox

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

Last change on this file since 67183 was 67183, checked in by vboxsync, 8 years ago

vfsmemory.cpp: Don't assert negative offset parameter in read and write. The code deals just fine with seeks.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 28.7 KB
Line 
1/* $Id: vfsmemory.cpp 67183 2017-05-31 20:31:54Z vboxsync $ */
2/** @file
3 * IPRT - Virtual File System, Memory Backed VFS.
4 */
5
6/*
7 * Copyright (C) 2010-2016 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 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_OFFSETOF(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 += cbLeftToWrite;
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,pfnPollOne}
554 */
555static DECLCALLBACK(int) rtVfsMemFile_PollOne(void *pvThis, uint32_t fEvents, RTMSINTERVAL cMillies, bool fIntr,
556 uint32_t *pfRetEvents)
557{
558 NOREF(pvThis);
559 int rc;
560 if (fEvents != RTPOLL_EVT_ERROR)
561 {
562 *pfRetEvents = fEvents & ~RTPOLL_EVT_ERROR;
563 rc = VINF_SUCCESS;
564 }
565 else
566 rc = RTVfsUtilDummyPollOne(fEvents, cMillies, fIntr, pfRetEvents);
567 return rc;
568}
569
570
571/**
572 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnTell}
573 */
574static DECLCALLBACK(int) rtVfsMemFile_Tell(void *pvThis, PRTFOFF poffActual)
575{
576 PRTVFSMEMFILE pThis = (PRTVFSMEMFILE)pvThis;
577 *poffActual = pThis->offCurPos;
578 return VINF_SUCCESS;
579}
580
581
582/**
583 * @interface_method_impl{RTVFSOBJSETOPS,pfnMode}
584 */
585static DECLCALLBACK(int) rtVfsMemFile_SetMode(void *pvThis, RTFMODE fMode, RTFMODE fMask)
586{
587 PRTVFSMEMFILE pThis = (PRTVFSMEMFILE)pvThis;
588 pThis->Base.ObjInfo.Attr.fMode = (pThis->Base.ObjInfo.Attr.fMode & ~fMask) | fMode;
589 return VINF_SUCCESS;
590}
591
592
593/**
594 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetTimes}
595 */
596static DECLCALLBACK(int) rtVfsMemFile_SetTimes(void *pvThis, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
597 PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
598{
599 PRTVFSMEMFILE pThis = (PRTVFSMEMFILE)pvThis;
600
601 if (pAccessTime)
602 pThis->Base.ObjInfo.AccessTime = *pAccessTime;
603 if (pModificationTime)
604 pThis->Base.ObjInfo.ModificationTime = *pModificationTime;
605 if (pChangeTime)
606 pThis->Base.ObjInfo.ChangeTime = *pChangeTime;
607 if (pBirthTime)
608 pThis->Base.ObjInfo.BirthTime = *pBirthTime;
609
610 return VINF_SUCCESS;
611}
612
613
614/**
615 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetOwner}
616 */
617static DECLCALLBACK(int) rtVfsMemFile_SetOwner(void *pvThis, RTUID uid, RTGID gid)
618{
619 PRTVFSMEMFILE pThis = (PRTVFSMEMFILE)pvThis;
620
621 if (uid != NIL_RTUID)
622 pThis->Base.ObjInfo.Attr.u.Unix.uid = uid;
623 if (gid != NIL_RTUID)
624 pThis->Base.ObjInfo.Attr.u.Unix.gid = gid;
625
626 return VINF_SUCCESS;
627}
628
629
630/**
631 * @interface_method_impl{RTVFSFILEOPS,pfnSeek}
632 */
633static DECLCALLBACK(int) rtVfsMemFile_Seek(void *pvThis, RTFOFF offSeek, unsigned uMethod, PRTFOFF poffActual)
634{
635 PRTVFSMEMFILE pThis = (PRTVFSMEMFILE)pvThis;
636
637 /*
638 * Seek relative to which position.
639 */
640 uint64_t offWrt;
641 switch (uMethod)
642 {
643 case RTFILE_SEEK_BEGIN:
644 offWrt = 0;
645 break;
646
647 case RTFILE_SEEK_CURRENT:
648 offWrt = pThis->offCurPos;
649 break;
650
651 case RTFILE_SEEK_END:
652 offWrt = pThis->Base.ObjInfo.cbObject;
653 break;
654
655 default:
656 return VERR_INTERNAL_ERROR_5;
657 }
658
659 /*
660 * Calc new position, take care to stay without bounds.
661 */
662 uint64_t offNew;
663 if (offSeek == 0)
664 offNew = offWrt;
665 else if (offSeek > 0)
666 {
667 offNew = offWrt + offSeek;
668 if ( offNew < offWrt
669 || offNew > RTFOFF_MAX)
670 offNew = RTFOFF_MAX;
671 }
672 else if ((uint64_t)-offSeek < offWrt)
673 offNew = offWrt + offSeek;
674 else
675 offNew = 0;
676
677 /*
678 * Update the state and set return value.
679 */
680 if ( pThis->pCurExt
681 && pThis->pCurExt->off - offNew >= pThis->pCurExt->cb)
682 pThis->pCurExt = NULL;
683 pThis->offCurPos = offNew;
684
685 *poffActual = offNew;
686 return VINF_SUCCESS;
687}
688
689
690/**
691 * @interface_method_impl{RTVFSFILEOPS,pfnQuerySize}
692 */
693static DECLCALLBACK(int) rtVfsMemFile_QuerySize(void *pvThis, uint64_t *pcbFile)
694{
695 PRTVFSMEMFILE pThis = (PRTVFSMEMFILE)pvThis;
696 *pcbFile = pThis->Base.ObjInfo.cbObject;
697 return VINF_SUCCESS;
698}
699
700
701/**
702 * Memory file operations.
703 */
704DECL_HIDDEN_CONST(const RTVFSFILEOPS) g_rtVfsMemFileOps =
705{
706 { /* Stream */
707 { /* Obj */
708 RTVFSOBJOPS_VERSION,
709 RTVFSOBJTYPE_FILE,
710 "MemFile",
711 rtVfsMemFile_Close,
712 rtVfsMemFile_QueryInfo,
713 RTVFSOBJOPS_VERSION
714 },
715 RTVFSIOSTREAMOPS_VERSION,
716 RTVFSIOSTREAMOPS_FEAT_NO_SG,
717 rtVfsMemFile_Read,
718 rtVfsMemFile_Write,
719 rtVfsMemFile_Flush,
720 rtVfsMemFile_PollOne,
721 rtVfsMemFile_Tell,
722 NULL /*Skip*/,
723 NULL /*ZeroFill*/,
724 RTVFSIOSTREAMOPS_VERSION,
725 },
726 RTVFSFILEOPS_VERSION,
727 /*RTVFSIOFILEOPS_FEAT_NO_AT_OFFSET*/ 0,
728 { /* ObjSet */
729 RTVFSOBJSETOPS_VERSION,
730 RT_OFFSETOF(RTVFSFILEOPS, Stream.Obj) - RT_OFFSETOF(RTVFSFILEOPS, ObjSet),
731 rtVfsMemFile_SetMode,
732 rtVfsMemFile_SetTimes,
733 rtVfsMemFile_SetOwner,
734 RTVFSOBJSETOPS_VERSION
735 },
736 rtVfsMemFile_Seek,
737 rtVfsMemFile_QuerySize,
738 RTVFSFILEOPS_VERSION
739};
740
741
742/**
743 * Initialize the RTVFSMEMFILE::Base.ObjInfo specific members.
744 *
745 * @param pObjInfo The object info to init.
746 * @param cbObject The object size set.
747 */
748static void rtVfsMemInitObjInfo(PRTFSOBJINFO pObjInfo, uint64_t cbObject)
749{
750 pObjInfo->cbObject = cbObject;
751 pObjInfo->cbAllocated = cbObject;
752 pObjInfo->Attr.fMode = RTFS_DOS_NT_NORMAL | RTFS_TYPE_FILE | RTFS_UNIX_IRWXU;
753 pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX;
754 pObjInfo->Attr.u.Unix.uid = NIL_RTUID;
755 pObjInfo->Attr.u.Unix.gid = NIL_RTGID;
756 pObjInfo->Attr.u.Unix.cHardlinks = 1;
757 pObjInfo->Attr.u.Unix.INodeIdDevice = 0;
758 pObjInfo->Attr.u.Unix.INodeId = 0;
759 pObjInfo->Attr.u.Unix.fFlags = 0;
760 pObjInfo->Attr.u.Unix.GenerationId = 0;
761 pObjInfo->Attr.u.Unix.Device = 0;
762 RTTimeNow(&pObjInfo->AccessTime);
763 pObjInfo->ModificationTime = pObjInfo->AccessTime;
764 pObjInfo->ChangeTime = pObjInfo->AccessTime;
765 pObjInfo->BirthTime = pObjInfo->AccessTime;
766}
767
768
769/**
770 * Initialize the RTVFSMEMFILE specific members.
771 *
772 * @param pThis The memory file to initialize.
773 * @param cbObject The object size for estimating extent size.
774 * @param fFlags The user specified flags.
775 */
776static void rtVfsMemFileInit(PRTVFSMEMFILE pThis, RTFOFF cbObject, uint32_t fFlags)
777{
778 pThis->offCurPos = 0;
779 pThis->pCurExt = NULL;
780 RTListInit(&pThis->ExtentHead);
781 if (cbObject <= 0)
782 pThis->cbExtent = _4K;
783 else if (cbObject < RTVFSMEM_MAX_EXTENT_SIZE)
784 pThis->cbExtent = fFlags & RTFILE_O_WRITE ? _4K : cbObject;
785 else
786 pThis->cbExtent = RTVFSMEM_MAX_EXTENT_SIZE;
787}
788
789
790/**
791 * Rewinds the file to position 0 and clears the WRITE flag if necessary.
792 *
793 * @param pThis The memory file instance.
794 * @param fFlags The user specified flags.
795 */
796static void rtVfsMemFileResetAndFixWriteFlag(PRTVFSMEMFILE pThis, uint32_t fFlags)
797{
798 pThis->pCurExt = RTListGetFirst(&pThis->ExtentHead, RTVFSMEMEXTENT, Entry);
799 pThis->offCurPos = 0;
800
801 if (!(fFlags & RTFILE_O_WRITE))
802 {
803 /** @todo clear RTFILE_O_WRITE from the resulting. */
804 }
805}
806
807
808RTDECL(int) RTVfsMemFileCreate(RTVFSIOSTREAM hVfsIos, size_t cbEstimate, PRTVFSFILE phVfsFile)
809{
810 /*
811 * Create a memory file instance and set the extension size according to the
812 * buffer size. Add the WRITE flag so we can use normal write APIs for
813 * copying the buffer.
814 */
815 RTVFSFILE hVfsFile;
816 PRTVFSMEMFILE pThis;
817 int rc = RTVfsNewFile(&g_rtVfsMemFileOps, sizeof(*pThis), RTFILE_O_READ | RTFILE_O_WRITE, NIL_RTVFS, NIL_RTVFSLOCK,
818 &hVfsFile, (void **)&pThis);
819 if (RT_SUCCESS(rc))
820 {
821 rtVfsMemInitObjInfo(&pThis->Base.ObjInfo, 0);
822 rtVfsMemFileInit(pThis, cbEstimate, RTFILE_O_READ | RTFILE_O_WRITE);
823
824 if (hVfsIos != NIL_RTVFSIOSTREAM)
825 {
826 RTVFSIOSTREAM hVfsIosDst = RTVfsFileToIoStream(hVfsFile);
827 rc = RTVfsUtilPumpIoStreams(hVfsIos, hVfsIosDst, pThis->cbExtent);
828 RTVfsIoStrmRelease(hVfsIosDst);
829 }
830
831 if (RT_SUCCESS(rc))
832 {
833 *phVfsFile = hVfsFile;
834 return VINF_SUCCESS;
835 }
836
837 RTVfsFileRelease(hVfsFile);
838 }
839 return rc;
840}
841
842
843RTDECL(int) RTVfsMemIoStrmCreate(RTVFSIOSTREAM hVfsIos, size_t cbEstimate, PRTVFSIOSTREAM phVfsIos)
844{
845 RTVFSFILE hVfsFile;
846 int rc = RTVfsMemFileCreate(hVfsIos, cbEstimate, &hVfsFile);
847 if (RT_SUCCESS(rc))
848 {
849 *phVfsIos = RTVfsFileToIoStream(hVfsFile);
850 AssertStmt(*phVfsIos != NIL_RTVFSIOSTREAM, rc = VERR_INTERNAL_ERROR_2);
851 RTVfsFileRelease(hVfsFile);
852 }
853 return rc;
854}
855
856
857RTDECL(int) RTVfsFileFromBuffer(uint32_t fFlags, void const *pvBuf, size_t cbBuf, PRTVFSFILE phVfsFile)
858{
859 /*
860 * Create a memory file instance and set the extension size according to the
861 * buffer size. Add the WRITE flag so we can use normal write APIs for
862 * copying the buffer.
863 */
864 RTVFSFILE hVfsFile;
865 PRTVFSMEMFILE pThis;
866 int rc = RTVfsNewFile(&g_rtVfsMemFileOps, sizeof(*pThis), fFlags | RTFILE_O_WRITE, NIL_RTVFS, NIL_RTVFSLOCK,
867 &hVfsFile, (void **)&pThis);
868 if (RT_SUCCESS(rc))
869 {
870 rtVfsMemInitObjInfo(&pThis->Base.ObjInfo, cbBuf);
871 rtVfsMemFileInit(pThis, cbBuf, fFlags);
872
873 /*
874 * Copy the buffer and reposition the file pointer to the start.
875 */
876 rc = RTVfsFileWrite(hVfsFile, pvBuf, cbBuf, NULL);
877 if (RT_SUCCESS(rc))
878 {
879 rtVfsMemFileResetAndFixWriteFlag(pThis, fFlags);
880 *phVfsFile = hVfsFile;
881 return VINF_SUCCESS;
882 }
883 RTVfsFileRelease(hVfsFile);
884 }
885 return rc;
886}
887
888
889RTDECL(int) RTVfsIoStrmFromBuffer(uint32_t fFlags, void const *pvBuf, size_t cbBuf, PRTVFSIOSTREAM phVfsIos)
890{
891 RTVFSFILE hVfsFile;
892 int rc = RTVfsFileFromBuffer(fFlags, pvBuf, cbBuf, &hVfsFile);
893 if (RT_SUCCESS(rc))
894 {
895 *phVfsIos = RTVfsFileToIoStream(hVfsFile);
896 RTVfsFileRelease(hVfsFile);
897 }
898 return rc;
899}
900
901
902RTDECL(int) RTVfsMemorizeIoStreamAsFile(RTVFSIOSTREAM hVfsIos, uint32_t fFlags, PRTVFSFILE phVfsFile)
903{
904 /*
905 * Create a memory file instance and try set the extension size to match
906 * the length of the I/O stream.
907 */
908 RTFSOBJINFO ObjInfo;
909 int rc = RTVfsIoStrmQueryInfo(hVfsIos, &ObjInfo, RTFSOBJATTRADD_UNIX);
910 if (RT_SUCCESS(rc))
911 {
912 RTVFSFILE hVfsFile;
913 PRTVFSMEMFILE pThis;
914 rc = RTVfsNewFile(&g_rtVfsMemFileOps, sizeof(*pThis), fFlags | RTFILE_O_WRITE, NIL_RTVFS, NIL_RTVFSLOCK,
915 &hVfsFile, (void **)&pThis);
916 if (RT_SUCCESS(rc))
917 {
918 pThis->Base.ObjInfo = ObjInfo;
919 rtVfsMemFileInit(pThis, ObjInfo.cbObject, fFlags);
920
921 /*
922 * Copy the stream.
923 */
924 RTVFSIOSTREAM hVfsIosDst = RTVfsFileToIoStream(hVfsFile);
925 rc = RTVfsUtilPumpIoStreams(hVfsIos, hVfsIosDst, pThis->cbExtent);
926 RTVfsIoStrmRelease(hVfsIosDst);
927 if (RT_SUCCESS(rc))
928 {
929 rtVfsMemFileResetAndFixWriteFlag(pThis, fFlags);
930 *phVfsFile = hVfsFile;
931 return VINF_SUCCESS;
932 }
933 RTVfsFileRelease(hVfsFile);
934 }
935 }
936 return rc;
937}
938
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