VirtualBox

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

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