VirtualBox

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

Last change on this file since 72118 was 70307, checked in by vboxsync, 7 years ago

VDVfs.cpp: Nit.

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