VirtualBox

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

Last change on this file since 99196 was 98103, checked in by vboxsync, 23 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 23.8 KB
Line 
1/* $Id: VDVfs.cpp 98103 2023-01-17 14:15:46Z 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, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbRead)
344{
345 PVDVFSFILE 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 int rc = VINF_SUCCESS;
367 size_t cbLeftToRead = pSgBuf->paSegs[0].cbSeg;
368 if (offUnsigned + cbLeftToRead <= cbImage)
369 {
370 if (pcbRead)
371 *pcbRead = cbLeftToRead;
372 }
373 else
374 {
375 if (!pcbRead)
376 return VERR_EOF;
377 *pcbRead = cbLeftToRead = (size_t)(cbImage - offUnsigned);
378 rc = VINF_EOF;
379 }
380
381 /*
382 * Ok, we've got a valid stretch within the file. Do the reading.
383 */
384 if (cbLeftToRead > 0)
385 {
386 int rc2 = vdReadHelper(pThis->pDisk, offUnsigned, pSgBuf->paSegs[0].pvSeg, cbLeftToRead);
387 if (RT_SUCCESS(rc2))
388 offUnsigned += cbLeftToRead;
389 else
390 rc = rc2;
391 }
392
393 pThis->offCurPos = offUnsigned;
394 return rc;
395}
396
397
398/**
399 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnWrite}
400 */
401static DECLCALLBACK(int) vdVfsFile_Write(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbWritten)
402{
403 PVDVFSFILE pThis = (PVDVFSFILE)pvThis;
404
405 Assert(pSgBuf->cSegs == 1);
406 NOREF(fBlocking);
407
408 /*
409 * Find the current position and check if it's within the volume.
410 * Writing beyond the end of a volume is not supported.
411 */
412 uint64_t offUnsigned = off < 0 ? pThis->offCurPos : (uint64_t)off;
413 uint64_t const cbImage = VDGetSize(pThis->pDisk, VD_LAST_IMAGE);
414 if (offUnsigned >= cbImage)
415 {
416 if (pcbWritten)
417 {
418 *pcbWritten = 0;
419 pThis->offCurPos = cbImage;
420 }
421 return VERR_EOF;
422 }
423
424 size_t cbLeftToWrite;
425 if (offUnsigned + pSgBuf->paSegs[0].cbSeg <= cbImage)
426 {
427 cbLeftToWrite = pSgBuf->paSegs[0].cbSeg;
428 if (pcbWritten)
429 *pcbWritten = cbLeftToWrite;
430 }
431 else
432 {
433 if (!pcbWritten)
434 return VERR_EOF;
435 *pcbWritten = cbLeftToWrite = (size_t)(cbImage - offUnsigned);
436 }
437
438 /*
439 * Ok, we've got a valid stretch within the file. Do the reading.
440 */
441 int rc = VINF_SUCCESS;
442 if (cbLeftToWrite > 0)
443 {
444 rc = vdWriteHelper(pThis->pDisk, offUnsigned, pSgBuf->paSegs[0].pvSeg, cbLeftToWrite);
445 if (RT_SUCCESS(rc))
446 offUnsigned += cbLeftToWrite;
447 }
448
449 pThis->offCurPos = offUnsigned;
450 return rc;
451}
452
453
454/**
455 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnFlush}
456 */
457static DECLCALLBACK(int) vdVfsFile_Flush(void *pvThis)
458{
459 PVDVFSFILE pThis = (PVDVFSFILE)pvThis;
460 return VDFlush(pThis->pDisk);
461}
462
463
464/**
465 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnTell}
466 */
467static DECLCALLBACK(int) vdVfsFile_Tell(void *pvThis, PRTFOFF poffActual)
468{
469 PVDVFSFILE pThis = (PVDVFSFILE)pvThis;
470 *poffActual = pThis->offCurPos;
471 return VINF_SUCCESS;
472}
473
474
475/**
476 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetMode}
477 */
478static DECLCALLBACK(int) vdVfsFile_SetMode(void *pvThis, RTFMODE fMode, RTFMODE fMask)
479{
480 NOREF(pvThis);
481 NOREF(fMode);
482 NOREF(fMask);
483 return VERR_NOT_SUPPORTED;
484}
485
486
487/**
488 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetTimes}
489 */
490static DECLCALLBACK(int) vdVfsFile_SetTimes(void *pvThis, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
491 PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
492{
493 NOREF(pvThis);
494 NOREF(pAccessTime);
495 NOREF(pModificationTime);
496 NOREF(pChangeTime);
497 NOREF(pBirthTime);
498 return VERR_NOT_SUPPORTED;
499}
500
501
502/**
503 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetOwner}
504 */
505static DECLCALLBACK(int) vdVfsFile_SetOwner(void *pvThis, RTUID uid, RTGID gid)
506{
507 NOREF(pvThis);
508 NOREF(uid);
509 NOREF(gid);
510 return VERR_NOT_SUPPORTED;
511}
512
513
514/**
515 * @interface_method_impl{RTVFSFILEOPS,pfnSeek}
516 */
517static DECLCALLBACK(int) vdVfsFile_Seek(void *pvThis, RTFOFF offSeek, unsigned uMethod, PRTFOFF poffActual)
518{
519 PVDVFSFILE pThis = (PVDVFSFILE)pvThis;
520
521 /*
522 * Seek relative to which position.
523 */
524 uint64_t offWrt;
525 switch (uMethod)
526 {
527 case RTFILE_SEEK_BEGIN:
528 offWrt = 0;
529 break;
530
531 case RTFILE_SEEK_CURRENT:
532 offWrt = pThis->offCurPos;
533 break;
534
535 case RTFILE_SEEK_END:
536 offWrt = VDGetSize(pThis->pDisk, VD_LAST_IMAGE);
537 break;
538
539 default:
540 return VERR_INTERNAL_ERROR_5;
541 }
542
543 /*
544 * Calc new position, take care to stay without bounds.
545 */
546 uint64_t offNew;
547 if (offSeek == 0)
548 offNew = offWrt;
549 else if (offSeek > 0)
550 {
551 offNew = offWrt + offSeek;
552 if ( offNew < offWrt
553 || offNew > RTFOFF_MAX)
554 offNew = RTFOFF_MAX;
555 }
556 else if ((uint64_t)-offSeek < offWrt)
557 offNew = offWrt + offSeek;
558 else
559 offNew = 0;
560
561 /*
562 * Update the state and set return value.
563 */
564 pThis->offCurPos = offNew;
565
566 *poffActual = offNew;
567 return VINF_SUCCESS;
568}
569
570
571/**
572 * @interface_method_impl{RTVFSFILEOPS,pfnQuerySize}
573 */
574static DECLCALLBACK(int) vdVfsFile_QuerySize(void *pvThis, uint64_t *pcbFile)
575{
576 PVDVFSFILE pThis = (PVDVFSFILE)pvThis;
577 *pcbFile = VDGetSize(pThis->pDisk, VD_LAST_IMAGE);
578 return VINF_SUCCESS;
579}
580
581
582/**
583 * Standard file operations.
584 */
585DECL_HIDDEN_CONST(const RTVFSFILEOPS) g_vdVfsStdFileOps =
586{
587 { /* Stream */
588 { /* Obj */
589 RTVFSOBJOPS_VERSION,
590 RTVFSOBJTYPE_FILE,
591 "VDFile",
592 vdVfsFile_Close,
593 vdVfsFile_QueryInfo,
594 NULL,
595 RTVFSOBJOPS_VERSION
596 },
597 RTVFSIOSTREAMOPS_VERSION,
598 RTVFSIOSTREAMOPS_FEAT_NO_SG,
599 vdVfsFile_Read,
600 vdVfsFile_Write,
601 vdVfsFile_Flush,
602 NULL /*PollOne*/,
603 vdVfsFile_Tell,
604 NULL /*Skip*/,
605 NULL /*ZeroFill*/,
606 RTVFSIOSTREAMOPS_VERSION,
607 },
608 RTVFSFILEOPS_VERSION,
609 /*RTVFSIOFILEOPS_FEAT_NO_AT_OFFSET*/ 0,
610 { /* ObjSet */
611 RTVFSOBJSETOPS_VERSION,
612 RT_UOFFSETOF(RTVFSFILEOPS, ObjSet) - RT_UOFFSETOF(RTVFSFILEOPS, Stream.Obj),
613 vdVfsFile_SetMode,
614 vdVfsFile_SetTimes,
615 vdVfsFile_SetOwner,
616 RTVFSOBJSETOPS_VERSION
617 },
618 vdVfsFile_Seek,
619 vdVfsFile_QuerySize,
620 NULL /*SetSize*/,
621 NULL /*QueryMaxSize*/,
622 RTVFSFILEOPS_VERSION
623};
624
625
626VBOXDDU_DECL(int) VDCreateVfsFileFromDisk(PVDISK pDisk, uint32_t fFlags,
627 PRTVFSFILE phVfsFile)
628{
629 AssertPtrReturn(pDisk, VERR_INVALID_HANDLE);
630 AssertPtrReturn(phVfsFile, VERR_INVALID_POINTER);
631 AssertReturn((fFlags & ~VD_VFSFILE_FLAGS_MASK) == 0, VERR_INVALID_PARAMETER);
632
633 /*
634 * Create the volume file.
635 */
636 RTVFSFILE hVfsFile;
637 PVDVFSFILE pThis;
638 int rc = RTVfsNewFile(&g_vdVfsStdFileOps, sizeof(*pThis), RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_WRITE,
639 NIL_RTVFS, NIL_RTVFSLOCK, &hVfsFile, (void **)&pThis);
640 if (RT_SUCCESS(rc))
641 {
642 pThis->offCurPos = 0;
643 pThis->pDisk = pDisk;
644 pThis->fFlags = fFlags;
645
646 *phVfsFile = hVfsFile;
647 return VINF_SUCCESS;
648 }
649
650 return rc;
651}
652
653
654/**
655 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnValidate}
656 */
657static DECLCALLBACK(int) vdVfsChain_Validate(PCRTVFSCHAINELEMENTREG pProviderReg, PRTVFSCHAINSPEC pSpec,
658 PRTVFSCHAINELEMSPEC pElement, uint32_t *poffError, PRTERRINFO pErrInfo)
659{
660 RT_NOREF(pProviderReg, pSpec);
661
662 /*
663 * Basic checks.
664 */
665 if (pElement->enmTypeIn != RTVFSOBJTYPE_INVALID)
666 return VERR_VFS_CHAIN_MUST_BE_FIRST_ELEMENT;
667 if ( pElement->enmType != RTVFSOBJTYPE_FILE
668 && pElement->enmType != RTVFSOBJTYPE_IO_STREAM)
669 return VERR_VFS_CHAIN_ONLY_FILE_OR_IOS;
670
671 if (pElement->cArgs < 1)
672 return VERR_VFS_CHAIN_AT_LEAST_ONE_ARG;
673
674 /*
675 * Parse the flag if present, save in pElement->uProvider.
676 */
677 uint32_t fFlags = (pSpec->fOpenFile & RTFILE_O_ACCESS_MASK) == RTFILE_O_READ
678 ? VD_OPEN_FLAGS_READONLY : VD_OPEN_FLAGS_NORMAL;
679 if (pElement->cArgs > 1)
680 {
681 pElement->paArgs[pElement->cArgs - 1].uProvider = true; /* indicates flags */
682 const char *psz = pElement->paArgs[pElement->cArgs - 1].psz;
683 if (*psz)
684 {
685 if ( !strcmp(psz, "ro")
686 || !strcmp(psz, "r"))
687 {
688 fFlags &= ~(VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_NORMAL);
689 fFlags |= VD_OPEN_FLAGS_READONLY;
690 }
691 else if (!strcmp(psz, "rw"))
692 {
693 fFlags &= ~(VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_NORMAL);
694 fFlags |= VD_OPEN_FLAGS_NORMAL;
695 }
696 else if (strlen(psz) <= 4)
697 {
698 *poffError = pElement->paArgs[pElement->cArgs - 1].offSpec;
699 return RTErrInfoSet(pErrInfo, VERR_VFS_CHAIN_INVALID_ARGUMENT, "Expected 'ro' or 'rw' as argument");
700 }
701 else
702 pElement->paArgs[pElement->cArgs - 1].uProvider = false; /* indicates no flags */
703 }
704 }
705
706 pElement->uProvider = fFlags;
707 if ( pElement->cArgs > 2
708 || (pElement->cArgs == 2 && !pElement->paArgs[pElement->cArgs - 1].uProvider))
709 pElement->uProvider |= RT_BIT_64(63);
710 return VINF_SUCCESS;
711}
712
713
714/**
715 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnInstantiate}
716 */
717static DECLCALLBACK(int) vdVfsChain_Instantiate(PCRTVFSCHAINELEMENTREG pProviderReg, PCRTVFSCHAINSPEC pSpec,
718 PCRTVFSCHAINELEMSPEC pElement, RTVFSOBJ hPrevVfsObj,
719 PRTVFSOBJ phVfsObj, uint32_t *poffError, PRTERRINFO pErrInfo)
720{
721 RT_NOREF(pProviderReg, pSpec, poffError, pErrInfo);
722 AssertReturn(hPrevVfsObj == NIL_RTVFSOBJ, VERR_VFS_CHAIN_IPE);
723
724 /* Determin the format. */
725 char *pszFormat = NULL;
726 VDTYPE enmType = VDTYPE_INVALID;
727 int rc = VDGetFormat(NULL, NULL, pElement->paArgs[0].psz, VDTYPE_INVALID, &pszFormat, &enmType);
728 if (RT_SUCCESS(rc))
729 {
730 PVDISK pDisk = NULL;
731 rc = VDCreate(NULL, enmType, &pDisk);
732 if (RT_SUCCESS(rc))
733 {
734 if (!(pElement->uProvider & RT_BIT_64(63)))
735 rc = VDOpen(pDisk, pszFormat, pElement->paArgs[0].psz, (uint32_t)pElement->uProvider, NULL);
736 else
737 {
738 uint32_t cChain = pElement->cArgs;
739 if (pElement->cArgs >= 2 && pElement->paArgs[pElement->cArgs - 1].uProvider != 0)
740 cChain--;
741 uint32_t const fFinal = (uint32_t)pElement->uProvider;
742 uint32_t const fReadOnly = (fFinal & ~(VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_NORMAL)) | VD_OPEN_FLAGS_READONLY;
743 uint32_t iChain;
744 for (iChain = 0; iChain < cChain && RT_SUCCESS(rc); iChain++)
745 rc = VDOpen(pDisk, pszFormat, pElement->paArgs[iChain].psz, iChain + 1 >= cChain ? fFinal : fReadOnly, NULL);
746 }
747 if (RT_SUCCESS(rc))
748 {
749 RTVFSFILE hVfsFile;
750 rc = VDCreateVfsFileFromDisk(pDisk, VD_VFSFILE_DESTROY_ON_RELEASE, &hVfsFile);
751 if (RT_SUCCESS(rc))
752 {
753 RTStrFree(pszFormat);
754
755 *phVfsObj = RTVfsObjFromFile(hVfsFile);
756 RTVfsFileRelease(hVfsFile);
757
758 if (*phVfsObj != NIL_RTVFSOBJ)
759 return VINF_SUCCESS;
760 return VERR_VFS_CHAIN_CAST_FAILED;
761 }
762 }
763 VDDestroy(pDisk);
764 }
765 RTStrFree(pszFormat);
766 }
767 return rc;
768}
769
770
771/**
772 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnCanReuseElement}
773 */
774static DECLCALLBACK(bool) vdVfsChain_CanReuseElement(PCRTVFSCHAINELEMENTREG pProviderReg,
775 PCRTVFSCHAINSPEC pSpec, PCRTVFSCHAINELEMSPEC pElement,
776 PCRTVFSCHAINSPEC pReuseSpec, PCRTVFSCHAINELEMSPEC pReuseElement)
777{
778 RT_NOREF(pProviderReg, pSpec, pElement, pReuseSpec, pReuseElement);
779 return false;
780}
781
782
783/** VFS chain element 'file'. */
784static RTVFSCHAINELEMENTREG g_rtVfsChainIsoFsVolReg =
785{
786 /* uVersion = */ RTVFSCHAINELEMENTREG_VERSION,
787 /* fReserved = */ 0,
788 /* pszName = */ "vd",
789 /* ListEntry = */ { NULL, NULL },
790 /* pszHelp = */ "Opens a container image using the VD API.\n"
791 "To open a snapshot chain, start with the root image and end with the more recent diff image.\n"
792 "The final argument can be a flag 'ro' or 'r' for read-only, 'rw' for read-write.",
793 /* pfnValidate = */ vdVfsChain_Validate,
794 /* pfnInstantiate = */ vdVfsChain_Instantiate,
795 /* pfnCanReuseElement = */ vdVfsChain_CanReuseElement,
796 /* uEndMarker = */ RTVFSCHAINELEMENTREG_VERSION
797};
798
799RTVFSCHAIN_AUTO_REGISTER_ELEMENT_PROVIDER(&g_rtVfsChainIsoFsVolReg, rtVfsChainIsoFsVolReg);
800
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