VirtualBox

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

Last change on this file since 82837 was 79965, checked in by vboxsync, 5 years ago

Storage: Added a desired format parameter to VDGetFormat() so Main can pass along the device type. Bumped the RAW backend down after the CUE and VISO to prevent (impossible) mixups of tiny files. Extended rawProbe() to look for a valid ISO-9660/UDF descriptor sequence on the image if the caller doesn't say it should be a floppy or hdd, only if that fail go by the extension. Note! the pfnProbe callback should probably get a score return parameter to indicate how confient the backend is about the probe result, as this would prevent the RAW backend from accidentally grabbing images which belongs to a plug-in. bugref:9151

  • 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 79965 2019-07-24 20:32:32Z vboxsync $ */
2/** @file
3 * Virtual Disk Container implementation. - VFS glue.
4 */
5
6/*
7 * Copyright (C) 2012-2019 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