VirtualBox

source: vbox/trunk/src/VBox/Storage/VDVfs.cpp

Last change on this file was 100908, checked in by vboxsync, 9 months ago

IPRT,Storage,Puel: Changed the pfnRead and pfnWrite VFS methods and the RTVfsIoStrmSgRead, RTVfsIoStrmSgWrite, RTVfsFileSgRead and RTVfsFileSgWrite APIs to advance pSgBuf and respect the incoming position just like RTFileSgRead & RTFileSgWrite.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 24.0 KB
Line 
1/* $Id: VDVfs.cpp 100908 2023-08-19 02:57:05Z vboxsync $ */
2/** @file
3 * Virtual Disk Container implementation. - VFS glue.
4 */
5
6/*
7 * Copyright (C) 2012-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#include <iprt/types.h>
33#include <iprt/assert.h>
34#include <iprt/mem.h>
35#include <iprt/err.h>
36#include <iprt/asm.h>
37#include <iprt/string.h>
38#include <iprt/file.h>
39#include <iprt/sg.h>
40#include <iprt/vfslowlevel.h>
41#include <iprt/poll.h>
42#include <VBox/vd.h>
43
44#include "VDInternal.h"
45
46
47/*********************************************************************************************************************************
48* Structures and Typedefs *
49*********************************************************************************************************************************/
50
51/**
52 * The internal data of a DVM volume I/O stream.
53 */
54typedef struct VDVFSFILE
55{
56 /** The volume the VFS file belongs to. */
57 PVDISK pDisk;
58 /** Current position. */
59 uint64_t offCurPos;
60 /** Flags given during creation. */
61 uint32_t fFlags;
62} VDVFSFILE;
63/** Pointer to a the internal data of a DVM volume file. */
64typedef VDVFSFILE *PVDVFSFILE;
65
66/**
67 * VD read helper taking care of unaligned accesses.
68 *
69 * @return VBox status code.
70 * @param pDisk VD disk container.
71 * @param off Offset to start reading from.
72 * @param pvBuf Pointer to the buffer to read into.
73 * @param cbRead Amount of bytes to read.
74 */
75static int vdReadHelper(PVDISK pDisk, uint64_t off, void *pvBuf, size_t cbRead)
76{
77 int rc;
78
79 /* Take direct route if the request is sector aligned. */
80 uint64_t const offMisalign = off & 511;
81 size_t const cbMisalign = (off + cbRead) & 511;
82 if ( !offMisalign
83 && !cbMisalign)
84 rc = VDRead(pDisk, off, pvBuf, cbRead);
85 else
86 {
87 uint8_t *pbBuf = (uint8_t *)pvBuf;
88 uint8_t abBuf[512];
89
90 /* Unaligned buffered read of head. Aligns the offset. */
91 if (offMisalign)
92 {
93 rc = VDRead(pDisk, off - offMisalign, abBuf, 512);
94 if (RT_SUCCESS(rc))
95 {
96 size_t const cbPart = RT_MIN(512 - offMisalign, cbRead);
97 memcpy(pbBuf, &abBuf[offMisalign], cbPart);
98 pbBuf += cbPart;
99 off += cbPart;
100 cbRead -= cbPart;
101 }
102 }
103 else
104 rc = VINF_SUCCESS;
105
106 /* Aligned direct read. */
107 if ( RT_SUCCESS(rc)
108 && cbRead >= 512)
109 {
110 Assert(!(off % 512));
111
112 size_t cbPart = cbRead - cbMisalign;
113 Assert(!(cbPart % 512));
114 rc = VDRead(pDisk, off, pbBuf, cbPart);
115 if (RT_SUCCESS(rc))
116 {
117 pbBuf += cbPart;
118 off += cbPart;
119 cbRead -= cbPart;
120 }
121 }
122
123 /* Unaligned buffered read of tail. */
124 if ( RT_SUCCESS(rc)
125 && cbRead)
126 {
127 Assert(cbRead == cbMisalign);
128 Assert(cbRead < 512);
129 Assert(!(off % 512));
130
131 rc = VDRead(pDisk, off, abBuf, 512);
132 if (RT_SUCCESS(rc))
133 memcpy(pbBuf, abBuf, cbRead);
134 }
135 }
136
137 return rc;
138}
139
140
141/**
142 * VD write helper taking care of unaligned accesses.
143 *
144 * @return VBox status code.
145 * @param pDisk VD disk container.
146 * @param off Offset to start writing to.
147 * @param pvSrc Pointer to the buffer to read from.
148 * @param cbWrite Amount of bytes to write.
149 */
150static int vdWriteHelper(PVDISK pDisk, uint64_t off, const void *pvSrc, size_t cbWrite)
151{
152 uint8_t const *pbSrc = (uint8_t const *)pvSrc;
153 uint8_t abBuf[4096];
154 int rc;
155
156 /*
157 * Take direct route if the request is sector aligned.
158 */
159 uint64_t const offMisalign = off & 511;
160 size_t const cbMisalign = (off + cbWrite) & 511;
161 if ( !offMisalign
162 && !cbMisalign)
163 {
164 if (RTListIsEmpty(&pDisk->ListFilterChainWrite))
165 rc = VDWrite(pDisk, off, pbSrc, cbWrite);
166 else
167 {
168 /* Filtered writes must be double buffered as the filter may need to modify the input buffer directly. */
169 do
170 {
171 size_t cbThisWrite = RT_MIN(cbWrite, sizeof(abBuf));
172 rc = VDWrite(pDisk, off, memcpy(abBuf, pbSrc, cbThisWrite), cbThisWrite);
173 if (RT_SUCCESS(rc))
174 {
175 pbSrc += cbThisWrite;
176 off += cbThisWrite;
177 cbWrite -= cbThisWrite;
178 }
179 else
180 break;
181 } while (cbWrite > 0);
182 }
183 }
184 else
185 {
186
187 /*
188 * Unaligned buffered read+write of head. Aligns the offset.
189 */
190 if (offMisalign)
191 {
192 rc = VDRead(pDisk, off - offMisalign, abBuf, 512);
193 if (RT_SUCCESS(rc))
194 {
195 size_t const cbPart = RT_MIN(512 - offMisalign, cbWrite);
196 memcpy(&abBuf[offMisalign], pbSrc, cbPart);
197 rc = VDWrite(pDisk, off - offMisalign, abBuf, 512);
198 if (RT_SUCCESS(rc))
199 {
200 pbSrc += cbPart;
201 off += cbPart;
202 cbWrite -= cbPart;
203 }
204 }
205 }
206 else
207 rc = VINF_SUCCESS;
208
209 /*
210 * Aligned direct write.
211 */
212 if ( RT_SUCCESS(rc)
213 && cbWrite >= 512)
214 {
215 Assert(!(off % 512));
216 size_t cbPart = cbWrite - cbMisalign;
217 Assert(!(cbPart % 512));
218
219 if (RTListIsEmpty(&pDisk->ListFilterChainWrite))
220 {
221 rc = VDWrite(pDisk, off, pbSrc, cbPart);
222 if (RT_SUCCESS(rc))
223 {
224 pbSrc += cbPart;
225 off += cbPart;
226 cbWrite -= cbPart;
227 }
228 }
229 else
230 {
231 /* Filtered writes must be double buffered as the filter may need to modify the input buffer directly. */
232 do
233 {
234 size_t cbThisWrite = RT_MIN(cbPart, sizeof(abBuf));
235 rc = VDWrite(pDisk, off, memcpy(abBuf, pbSrc, cbThisWrite), cbThisWrite);
236 if (RT_SUCCESS(rc))
237 {
238 pbSrc += cbThisWrite;
239 off += cbThisWrite;
240 cbWrite -= cbThisWrite;
241 cbPart -= cbThisWrite;
242 }
243 else
244 break;
245 } while (cbPart > 0);
246 }
247 }
248
249 /*
250 * Unaligned buffered read+write of tail.
251 */
252 if ( RT_SUCCESS(rc)
253 && cbWrite > 0)
254 {
255 Assert(cbWrite == cbMisalign);
256 Assert(cbWrite < 512);
257 Assert(!(off % 512));
258
259 rc = VDRead(pDisk, off, abBuf, 512);
260 if (RT_SUCCESS(rc))
261 {
262 memcpy(abBuf, pbSrc, cbWrite);
263 rc = VDWrite(pDisk, off, abBuf, 512);
264 }
265 }
266 }
267
268 return rc;
269}
270
271
272/**
273 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
274 */
275static DECLCALLBACK(int) vdVfsFile_Close(void *pvThis)
276{
277 PVDVFSFILE pThis = (PVDVFSFILE)pvThis;
278
279 if (pThis->fFlags & VD_VFSFILE_DESTROY_ON_RELEASE)
280 VDDestroy(pThis->pDisk);
281
282 return VINF_SUCCESS;
283}
284
285
286/**
287 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
288 */
289static DECLCALLBACK(int) vdVfsFile_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
290{
291 PVDVFSFILE pThis = (PVDVFSFILE)pvThis;
292 unsigned const cOpenImages = VDGetCount(pThis->pDisk);
293
294 pObjInfo->cbObject = VDGetSize(pThis->pDisk, cOpenImages - 1);
295 pObjInfo->cbAllocated = 0;
296 for (unsigned iImage = 0; iImage < cOpenImages; iImage++)
297 pObjInfo->cbAllocated += VDGetFileSize(pThis->pDisk, iImage);
298
299 /** @todo enumerate the disk images directly... */
300 RTTimeNow(&pObjInfo->AccessTime);
301 pObjInfo->BirthTime = pObjInfo->AccessTime;
302 pObjInfo->ChangeTime = pObjInfo->AccessTime;
303 pObjInfo->ModificationTime = pObjInfo->AccessTime;
304
305 pObjInfo->Attr.fMode = RTFS_DOS_NT_NORMAL | RTFS_TYPE_FILE | 0644;
306 pObjInfo->Attr.enmAdditional = enmAddAttr;
307 switch (enmAddAttr)
308 {
309 case RTFSOBJATTRADD_UNIX:
310 pObjInfo->Attr.u.Unix.uid = NIL_RTUID;
311 pObjInfo->Attr.u.Unix.gid = NIL_RTGID;
312 pObjInfo->Attr.u.Unix.cHardlinks = 1;
313 pObjInfo->Attr.u.Unix.INodeIdDevice = 0;
314 pObjInfo->Attr.u.Unix.INodeId = 0;
315 pObjInfo->Attr.u.Unix.fFlags = 0;
316 pObjInfo->Attr.u.Unix.GenerationId = 0;
317 pObjInfo->Attr.u.Unix.Device = 0;
318 break;
319
320 case RTFSOBJATTRADD_UNIX_OWNER:
321 pObjInfo->Attr.u.UnixOwner.uid = NIL_RTUID;
322 pObjInfo->Attr.u.UnixOwner.szName[0] = '\0';
323 break;
324 case RTFSOBJATTRADD_UNIX_GROUP:
325 pObjInfo->Attr.u.UnixGroup.gid = NIL_RTGID;
326 pObjInfo->Attr.u.UnixGroup.szName[0] = '\0';
327 break;
328 case RTFSOBJATTRADD_EASIZE:
329 pObjInfo->Attr.u.EASize.cb = 0;
330 break;
331
332 default:
333 AssertFailedReturn(VERR_INVALID_PARAMETER);
334 }
335
336 return VINF_SUCCESS;
337}
338
339
340/**
341 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnRead}
342 */
343static DECLCALLBACK(int) vdVfsFile_Read(void *pvThis, RTFOFF off, PRTSGBUF pSgBuf, bool fBlocking, size_t *pcbRead)
344{
345 PVDVFSFILE const pThis = (PVDVFSFILE)pvThis;
346
347 Assert(pSgBuf->cSegs == 1);
348 NOREF(fBlocking);
349
350 /*
351 * Find the current position and check if it's within the volume.
352 */
353 uint64_t offUnsigned = off < 0 ? pThis->offCurPos : (uint64_t)off;
354 uint64_t const cbImage = VDGetSize(pThis->pDisk, VD_LAST_IMAGE);
355 if (offUnsigned >= cbImage)
356 {
357 if (pcbRead)
358 {
359 *pcbRead = 0;
360 pThis->offCurPos = cbImage;
361 return VINF_EOF;
362 }
363 return VERR_EOF;
364 }
365
366 size_t cbSeg = 0;
367 void * const pvSeg = RTSgBufGetCurrentSegment(pSgBuf, ~(size_t)0, &cbSeg);
368
369 int rcRet = VINF_SUCCESS;
370 size_t cbToRead;
371 if (cbSeg <= cbImage - offUnsigned)
372 cbToRead = cbSeg;
373 else if (pcbRead)
374 {
375 cbToRead = (size_t)(cbImage - offUnsigned);
376 rcRet = VINF_EOF;
377 }
378 else
379 return VERR_EOF;
380
381 /*
382 * Ok, we've got a valid stretch within the file. Do the reading.
383 */
384 if (cbToRead > 0)
385 {
386 int rc2 = vdReadHelper(pThis->pDisk, offUnsigned, pvSeg, cbToRead);
387 if (RT_SUCCESS(rc2))
388 {
389 offUnsigned += cbToRead;
390 RTSgBufAdvance(pSgBuf, cbToRead);
391 }
392 else
393 {
394 cbToRead = 0;
395 rcRet = rc2;
396 }
397 }
398
399 pThis->offCurPos = offUnsigned;
400 if (pcbRead)
401 *pcbRead = cbToRead;
402 return rcRet;
403}
404
405
406/**
407 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnWrite}
408 */
409static DECLCALLBACK(int) vdVfsFile_Write(void *pvThis, RTFOFF off, PRTSGBUF pSgBuf, bool fBlocking, size_t *pcbWritten)
410{
411 PVDVFSFILE const pThis = (PVDVFSFILE)pvThis;
412
413 Assert(pSgBuf->cSegs == 1);
414 NOREF(fBlocking);
415
416 /*
417 * Find the current position and check if it's within the volume.
418 * Writing beyond the end of a volume is not supported.
419 */
420 uint64_t offUnsigned = off < 0 ? pThis->offCurPos : (uint64_t)off;
421 uint64_t const cbImage = VDGetSize(pThis->pDisk, VD_LAST_IMAGE);
422 if (offUnsigned >= cbImage)
423 {
424 if (pcbWritten)
425 {
426 *pcbWritten = 0;
427 pThis->offCurPos = cbImage;
428 }
429 return VERR_EOF;
430 }
431
432 size_t cbSeg = 0;
433 void const * const pvSeg = RTSgBufGetCurrentSegment(pSgBuf, ~(size_t)0, &cbSeg);
434
435 size_t cbToWrite;
436 if (cbSeg <= cbImage - offUnsigned)
437 cbToWrite = cbSeg;
438 else if (pcbWritten)
439 cbToWrite = (size_t)(cbImage - offUnsigned);
440 else
441 return VERR_EOF;
442
443 /*
444 * Ok, we've got a valid stretch within the file. Do the reading.
445 */
446 int rc = VINF_SUCCESS;
447 if (cbToWrite > 0)
448 {
449 rc = vdWriteHelper(pThis->pDisk, offUnsigned, pvSeg, cbToWrite);
450 if (RT_SUCCESS(rc))
451 {
452 offUnsigned += cbToWrite;
453 RTSgBufAdvance(pSgBuf, cbToWrite);
454 }
455 else
456 cbToWrite = 0;
457 }
458
459 pThis->offCurPos = offUnsigned;
460 if (pcbWritten)
461 *pcbWritten = cbToWrite;
462 return rc;
463}
464
465
466/**
467 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnFlush}
468 */
469static DECLCALLBACK(int) vdVfsFile_Flush(void *pvThis)
470{
471 PVDVFSFILE pThis = (PVDVFSFILE)pvThis;
472 return VDFlush(pThis->pDisk);
473}
474
475
476/**
477 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnTell}
478 */
479static DECLCALLBACK(int) vdVfsFile_Tell(void *pvThis, PRTFOFF poffActual)
480{
481 PVDVFSFILE pThis = (PVDVFSFILE)pvThis;
482 *poffActual = pThis->offCurPos;
483 return VINF_SUCCESS;
484}
485
486
487/**
488 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetMode}
489 */
490static DECLCALLBACK(int) vdVfsFile_SetMode(void *pvThis, RTFMODE fMode, RTFMODE fMask)
491{
492 NOREF(pvThis);
493 NOREF(fMode);
494 NOREF(fMask);
495 return VERR_NOT_SUPPORTED;
496}
497
498
499/**
500 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetTimes}
501 */
502static DECLCALLBACK(int) vdVfsFile_SetTimes(void *pvThis, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
503 PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
504{
505 NOREF(pvThis);
506 NOREF(pAccessTime);
507 NOREF(pModificationTime);
508 NOREF(pChangeTime);
509 NOREF(pBirthTime);
510 return VERR_NOT_SUPPORTED;
511}
512
513
514/**
515 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetOwner}
516 */
517static DECLCALLBACK(int) vdVfsFile_SetOwner(void *pvThis, RTUID uid, RTGID gid)
518{
519 NOREF(pvThis);
520 NOREF(uid);
521 NOREF(gid);
522 return VERR_NOT_SUPPORTED;
523}
524
525
526/**
527 * @interface_method_impl{RTVFSFILEOPS,pfnSeek}
528 */
529static DECLCALLBACK(int) vdVfsFile_Seek(void *pvThis, RTFOFF offSeek, unsigned uMethod, PRTFOFF poffActual)
530{
531 PVDVFSFILE pThis = (PVDVFSFILE)pvThis;
532
533 /*
534 * Seek relative to which position.
535 */
536 uint64_t offWrt;
537 switch (uMethod)
538 {
539 case RTFILE_SEEK_BEGIN:
540 offWrt = 0;
541 break;
542
543 case RTFILE_SEEK_CURRENT:
544 offWrt = pThis->offCurPos;
545 break;
546
547 case RTFILE_SEEK_END:
548 offWrt = VDGetSize(pThis->pDisk, VD_LAST_IMAGE);
549 break;
550
551 default:
552 return VERR_INTERNAL_ERROR_5;
553 }
554
555 /*
556 * Calc new position, take care to stay without bounds.
557 */
558 uint64_t offNew;
559 if (offSeek == 0)
560 offNew = offWrt;
561 else if (offSeek > 0)
562 {
563 offNew = offWrt + offSeek;
564 if ( offNew < offWrt
565 || offNew > RTFOFF_MAX)
566 offNew = RTFOFF_MAX;
567 }
568 else if ((uint64_t)-offSeek < offWrt)
569 offNew = offWrt + offSeek;
570 else
571 offNew = 0;
572
573 /*
574 * Update the state and set return value.
575 */
576 pThis->offCurPos = offNew;
577
578 *poffActual = offNew;
579 return VINF_SUCCESS;
580}
581
582
583/**
584 * @interface_method_impl{RTVFSFILEOPS,pfnQuerySize}
585 */
586static DECLCALLBACK(int) vdVfsFile_QuerySize(void *pvThis, uint64_t *pcbFile)
587{
588 PVDVFSFILE pThis = (PVDVFSFILE)pvThis;
589 *pcbFile = VDGetSize(pThis->pDisk, VD_LAST_IMAGE);
590 return VINF_SUCCESS;
591}
592
593
594/**
595 * Standard file operations.
596 */
597DECL_HIDDEN_CONST(const RTVFSFILEOPS) g_vdVfsStdFileOps =
598{
599 { /* Stream */
600 { /* Obj */
601 RTVFSOBJOPS_VERSION,
602 RTVFSOBJTYPE_FILE,
603 "VDFile",
604 vdVfsFile_Close,
605 vdVfsFile_QueryInfo,
606 NULL,
607 RTVFSOBJOPS_VERSION
608 },
609 RTVFSIOSTREAMOPS_VERSION,
610 RTVFSIOSTREAMOPS_FEAT_NO_SG,
611 vdVfsFile_Read,
612 vdVfsFile_Write,
613 vdVfsFile_Flush,
614 NULL /*PollOne*/,
615 vdVfsFile_Tell,
616 NULL /*Skip*/,
617 NULL /*ZeroFill*/,
618 RTVFSIOSTREAMOPS_VERSION,
619 },
620 RTVFSFILEOPS_VERSION,
621 /*RTVFSIOFILEOPS_FEAT_NO_AT_OFFSET*/ 0,
622 { /* ObjSet */
623 RTVFSOBJSETOPS_VERSION,
624 RT_UOFFSETOF(RTVFSFILEOPS, ObjSet) - RT_UOFFSETOF(RTVFSFILEOPS, Stream.Obj),
625 vdVfsFile_SetMode,
626 vdVfsFile_SetTimes,
627 vdVfsFile_SetOwner,
628 RTVFSOBJSETOPS_VERSION
629 },
630 vdVfsFile_Seek,
631 vdVfsFile_QuerySize,
632 NULL /*SetSize*/,
633 NULL /*QueryMaxSize*/,
634 RTVFSFILEOPS_VERSION
635};
636
637
638VBOXDDU_DECL(int) VDCreateVfsFileFromDisk(PVDISK pDisk, uint32_t fFlags,
639 PRTVFSFILE phVfsFile)
640{
641 AssertPtrReturn(pDisk, VERR_INVALID_HANDLE);
642 AssertPtrReturn(phVfsFile, VERR_INVALID_POINTER);
643 AssertReturn((fFlags & ~VD_VFSFILE_FLAGS_MASK) == 0, VERR_INVALID_PARAMETER);
644
645 /*
646 * Create the volume file.
647 */
648 RTVFSFILE hVfsFile;
649 PVDVFSFILE pThis;
650 int rc = RTVfsNewFile(&g_vdVfsStdFileOps, sizeof(*pThis), RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_WRITE,
651 NIL_RTVFS, NIL_RTVFSLOCK, &hVfsFile, (void **)&pThis);
652 if (RT_SUCCESS(rc))
653 {
654 pThis->offCurPos = 0;
655 pThis->pDisk = pDisk;
656 pThis->fFlags = fFlags;
657
658 *phVfsFile = hVfsFile;
659 return VINF_SUCCESS;
660 }
661
662 return rc;
663}
664
665
666/**
667 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnValidate}
668 */
669static DECLCALLBACK(int) vdVfsChain_Validate(PCRTVFSCHAINELEMENTREG pProviderReg, PRTVFSCHAINSPEC pSpec,
670 PRTVFSCHAINELEMSPEC pElement, uint32_t *poffError, PRTERRINFO pErrInfo)
671{
672 RT_NOREF(pProviderReg, pSpec);
673
674 /*
675 * Basic checks.
676 */
677 if (pElement->enmTypeIn != RTVFSOBJTYPE_INVALID)
678 return VERR_VFS_CHAIN_MUST_BE_FIRST_ELEMENT;
679 if ( pElement->enmType != RTVFSOBJTYPE_FILE
680 && pElement->enmType != RTVFSOBJTYPE_IO_STREAM)
681 return VERR_VFS_CHAIN_ONLY_FILE_OR_IOS;
682
683 if (pElement->cArgs < 1)
684 return VERR_VFS_CHAIN_AT_LEAST_ONE_ARG;
685
686 /*
687 * Parse the flag if present, save in pElement->uProvider.
688 */
689 uint32_t fFlags = (pSpec->fOpenFile & RTFILE_O_ACCESS_MASK) == RTFILE_O_READ
690 ? VD_OPEN_FLAGS_READONLY : VD_OPEN_FLAGS_NORMAL;
691 if (pElement->cArgs > 1)
692 {
693 pElement->paArgs[pElement->cArgs - 1].uProvider = true; /* indicates flags */
694 const char *psz = pElement->paArgs[pElement->cArgs - 1].psz;
695 if (*psz)
696 {
697 if ( !strcmp(psz, "ro")
698 || !strcmp(psz, "r"))
699 {
700 fFlags &= ~(VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_NORMAL);
701 fFlags |= VD_OPEN_FLAGS_READONLY;
702 }
703 else if (!strcmp(psz, "rw"))
704 {
705 fFlags &= ~(VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_NORMAL);
706 fFlags |= VD_OPEN_FLAGS_NORMAL;
707 }
708 else if (strlen(psz) <= 4)
709 {
710 *poffError = pElement->paArgs[pElement->cArgs - 1].offSpec;
711 return RTErrInfoSet(pErrInfo, VERR_VFS_CHAIN_INVALID_ARGUMENT, "Expected 'ro' or 'rw' as argument");
712 }
713 else
714 pElement->paArgs[pElement->cArgs - 1].uProvider = false; /* indicates no flags */
715 }
716 }
717
718 pElement->uProvider = fFlags;
719 if ( pElement->cArgs > 2
720 || (pElement->cArgs == 2 && !pElement->paArgs[pElement->cArgs - 1].uProvider))
721 pElement->uProvider |= RT_BIT_64(63);
722 return VINF_SUCCESS;
723}
724
725
726/**
727 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnInstantiate}
728 */
729static DECLCALLBACK(int) vdVfsChain_Instantiate(PCRTVFSCHAINELEMENTREG pProviderReg, PCRTVFSCHAINSPEC pSpec,
730 PCRTVFSCHAINELEMSPEC pElement, RTVFSOBJ hPrevVfsObj,
731 PRTVFSOBJ phVfsObj, uint32_t *poffError, PRTERRINFO pErrInfo)
732{
733 RT_NOREF(pProviderReg, pSpec, poffError, pErrInfo);
734 AssertReturn(hPrevVfsObj == NIL_RTVFSOBJ, VERR_VFS_CHAIN_IPE);
735
736 /* Determin the format. */
737 char *pszFormat = NULL;
738 VDTYPE enmType = VDTYPE_INVALID;
739 int rc = VDGetFormat(NULL, NULL, pElement->paArgs[0].psz, VDTYPE_INVALID, &pszFormat, &enmType);
740 if (RT_SUCCESS(rc))
741 {
742 PVDISK pDisk = NULL;
743 rc = VDCreate(NULL, enmType, &pDisk);
744 if (RT_SUCCESS(rc))
745 {
746 if (!(pElement->uProvider & RT_BIT_64(63)))
747 rc = VDOpen(pDisk, pszFormat, pElement->paArgs[0].psz, (uint32_t)pElement->uProvider, NULL);
748 else
749 {
750 uint32_t cChain = pElement->cArgs;
751 if (pElement->cArgs >= 2 && pElement->paArgs[pElement->cArgs - 1].uProvider != 0)
752 cChain--;
753 uint32_t const fFinal = (uint32_t)pElement->uProvider;
754 uint32_t const fReadOnly = (fFinal & ~(VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_NORMAL)) | VD_OPEN_FLAGS_READONLY;
755 uint32_t iChain;
756 for (iChain = 0; iChain < cChain && RT_SUCCESS(rc); iChain++)
757 rc = VDOpen(pDisk, pszFormat, pElement->paArgs[iChain].psz, iChain + 1 >= cChain ? fFinal : fReadOnly, NULL);
758 }
759 if (RT_SUCCESS(rc))
760 {
761 RTVFSFILE hVfsFile;
762 rc = VDCreateVfsFileFromDisk(pDisk, VD_VFSFILE_DESTROY_ON_RELEASE, &hVfsFile);
763 if (RT_SUCCESS(rc))
764 {
765 RTStrFree(pszFormat);
766
767 *phVfsObj = RTVfsObjFromFile(hVfsFile);
768 RTVfsFileRelease(hVfsFile);
769
770 if (*phVfsObj != NIL_RTVFSOBJ)
771 return VINF_SUCCESS;
772 return VERR_VFS_CHAIN_CAST_FAILED;
773 }
774 }
775 VDDestroy(pDisk);
776 }
777 RTStrFree(pszFormat);
778 }
779 return rc;
780}
781
782
783/**
784 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnCanReuseElement}
785 */
786static DECLCALLBACK(bool) vdVfsChain_CanReuseElement(PCRTVFSCHAINELEMENTREG pProviderReg,
787 PCRTVFSCHAINSPEC pSpec, PCRTVFSCHAINELEMSPEC pElement,
788 PCRTVFSCHAINSPEC pReuseSpec, PCRTVFSCHAINELEMSPEC pReuseElement)
789{
790 RT_NOREF(pProviderReg, pSpec, pElement, pReuseSpec, pReuseElement);
791 return false;
792}
793
794
795/** VFS chain element 'file'. */
796static RTVFSCHAINELEMENTREG g_rtVfsChainIsoFsVolReg =
797{
798 /* uVersion = */ RTVFSCHAINELEMENTREG_VERSION,
799 /* fReserved = */ 0,
800 /* pszName = */ "vd",
801 /* ListEntry = */ { NULL, NULL },
802 /* pszHelp = */ "Opens a container image using the VD API.\n"
803 "To open a snapshot chain, start with the root image and end with the more recent diff image.\n"
804 "The final argument can be a flag 'ro' or 'r' for read-only, 'rw' for read-write.",
805 /* pfnValidate = */ vdVfsChain_Validate,
806 /* pfnInstantiate = */ vdVfsChain_Instantiate,
807 /* pfnCanReuseElement = */ vdVfsChain_CanReuseElement,
808 /* uEndMarker = */ RTVFSCHAINELEMENTREG_VERSION
809};
810
811RTVFSCHAIN_AUTO_REGISTER_ELEMENT_PROVIDER(&g_rtVfsChainIsoFsVolReg, rtVfsChainIsoFsVolReg);
812
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use