VirtualBox

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

Last change on this file since 96014 was 94291, checked in by vboxsync, 3 years ago

IPRT,Storage: Adding RTVfsQueryLabel and internally a generic pfnQueryInfoEx method to the RTVFSOBJOPS function table. Untested implementation of the latter for iso/udf. bugref:9781

  • 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 94291 2022-03-17 13:29:52Z vboxsync $ */
2/** @file
3 * Virtual Disk Container implementation. - VFS glue.
4 */
5
6/*
7 * Copyright (C) 2012-2022 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 NULL,
585 RTVFSOBJOPS_VERSION
586 },
587 RTVFSIOSTREAMOPS_VERSION,
588 RTVFSIOSTREAMOPS_FEAT_NO_SG,
589 vdVfsFile_Read,
590 vdVfsFile_Write,
591 vdVfsFile_Flush,
592 NULL /*PollOne*/,
593 vdVfsFile_Tell,
594 NULL /*Skip*/,
595 NULL /*ZeroFill*/,
596 RTVFSIOSTREAMOPS_VERSION,
597 },
598 RTVFSFILEOPS_VERSION,
599 /*RTVFSIOFILEOPS_FEAT_NO_AT_OFFSET*/ 0,
600 { /* ObjSet */
601 RTVFSOBJSETOPS_VERSION,
602 RT_UOFFSETOF(RTVFSFILEOPS, ObjSet) - RT_UOFFSETOF(RTVFSFILEOPS, Stream.Obj),
603 vdVfsFile_SetMode,
604 vdVfsFile_SetTimes,
605 vdVfsFile_SetOwner,
606 RTVFSOBJSETOPS_VERSION
607 },
608 vdVfsFile_Seek,
609 vdVfsFile_QuerySize,
610 NULL /*SetSize*/,
611 NULL /*QueryMaxSize*/,
612 RTVFSFILEOPS_VERSION
613};
614
615
616VBOXDDU_DECL(int) VDCreateVfsFileFromDisk(PVDISK pDisk, uint32_t fFlags,
617 PRTVFSFILE phVfsFile)
618{
619 AssertPtrReturn(pDisk, VERR_INVALID_HANDLE);
620 AssertPtrReturn(phVfsFile, VERR_INVALID_POINTER);
621 AssertReturn((fFlags & ~VD_VFSFILE_FLAGS_MASK) == 0, VERR_INVALID_PARAMETER);
622
623 /*
624 * Create the volume file.
625 */
626 RTVFSFILE hVfsFile;
627 PVDVFSFILE pThis;
628 int rc = RTVfsNewFile(&g_vdVfsStdFileOps, sizeof(*pThis), RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_WRITE,
629 NIL_RTVFS, NIL_RTVFSLOCK, &hVfsFile, (void **)&pThis);
630 if (RT_SUCCESS(rc))
631 {
632 pThis->offCurPos = 0;
633 pThis->pDisk = pDisk;
634 pThis->fFlags = fFlags;
635
636 *phVfsFile = hVfsFile;
637 return VINF_SUCCESS;
638 }
639
640 return rc;
641}
642
643
644/**
645 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnValidate}
646 */
647static DECLCALLBACK(int) vdVfsChain_Validate(PCRTVFSCHAINELEMENTREG pProviderReg, PRTVFSCHAINSPEC pSpec,
648 PRTVFSCHAINELEMSPEC pElement, uint32_t *poffError, PRTERRINFO pErrInfo)
649{
650 RT_NOREF(pProviderReg, pSpec);
651
652 /*
653 * Basic checks.
654 */
655 if (pElement->enmTypeIn != RTVFSOBJTYPE_INVALID)
656 return VERR_VFS_CHAIN_MUST_BE_FIRST_ELEMENT;
657 if ( pElement->enmType != RTVFSOBJTYPE_FILE
658 && pElement->enmType != RTVFSOBJTYPE_IO_STREAM)
659 return VERR_VFS_CHAIN_ONLY_FILE_OR_IOS;
660
661 if (pElement->cArgs < 1)
662 return VERR_VFS_CHAIN_AT_LEAST_ONE_ARG;
663
664 /*
665 * Parse the flag if present, save in pElement->uProvider.
666 */
667 uint32_t fFlags = (pSpec->fOpenFile & RTFILE_O_ACCESS_MASK) == RTFILE_O_READ
668 ? VD_OPEN_FLAGS_READONLY : VD_OPEN_FLAGS_NORMAL;
669 if (pElement->cArgs > 1)
670 {
671 pElement->paArgs[pElement->cArgs - 1].uProvider = true; /* indicates flags */
672 const char *psz = pElement->paArgs[pElement->cArgs - 1].psz;
673 if (*psz)
674 {
675 if ( !strcmp(psz, "ro")
676 || !strcmp(psz, "r"))
677 {
678 fFlags &= ~(VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_NORMAL);
679 fFlags |= VD_OPEN_FLAGS_READONLY;
680 }
681 else if (!strcmp(psz, "rw"))
682 {
683 fFlags &= ~(VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_NORMAL);
684 fFlags |= VD_OPEN_FLAGS_NORMAL;
685 }
686 else if (strlen(psz) <= 4)
687 {
688 *poffError = pElement->paArgs[pElement->cArgs - 1].offSpec;
689 return RTErrInfoSet(pErrInfo, VERR_VFS_CHAIN_INVALID_ARGUMENT, "Expected 'ro' or 'rw' as argument");
690 }
691 else
692 pElement->paArgs[pElement->cArgs - 1].uProvider = false; /* indicates no flags */
693 }
694 }
695
696 pElement->uProvider = fFlags;
697 if ( pElement->cArgs > 2
698 || (pElement->cArgs == 2 && !pElement->paArgs[pElement->cArgs - 1].uProvider))
699 pElement->uProvider |= RT_BIT_64(63);
700 return VINF_SUCCESS;
701}
702
703
704/**
705 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnInstantiate}
706 */
707static DECLCALLBACK(int) vdVfsChain_Instantiate(PCRTVFSCHAINELEMENTREG pProviderReg, PCRTVFSCHAINSPEC pSpec,
708 PCRTVFSCHAINELEMSPEC pElement, RTVFSOBJ hPrevVfsObj,
709 PRTVFSOBJ phVfsObj, uint32_t *poffError, PRTERRINFO pErrInfo)
710{
711 RT_NOREF(pProviderReg, pSpec, poffError, pErrInfo);
712 AssertReturn(hPrevVfsObj == NIL_RTVFSOBJ, VERR_VFS_CHAIN_IPE);
713
714 /* Determin the format. */
715 char *pszFormat = NULL;
716 VDTYPE enmType = VDTYPE_INVALID;
717 int rc = VDGetFormat(NULL, NULL, pElement->paArgs[0].psz, VDTYPE_INVALID, &pszFormat, &enmType);
718 if (RT_SUCCESS(rc))
719 {
720 PVDISK pDisk = NULL;
721 rc = VDCreate(NULL, enmType, &pDisk);
722 if (RT_SUCCESS(rc))
723 {
724 if (!(pElement->uProvider & RT_BIT_64(63)))
725 rc = VDOpen(pDisk, pszFormat, pElement->paArgs[0].psz, (uint32_t)pElement->uProvider, NULL);
726 else
727 {
728 uint32_t cChain = pElement->cArgs;
729 if (pElement->cArgs >= 2 && pElement->paArgs[pElement->cArgs - 1].uProvider != 0)
730 cChain--;
731 uint32_t const fFinal = (uint32_t)pElement->uProvider;
732 uint32_t const fReadOnly = (fFinal & ~(VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_NORMAL)) | VD_OPEN_FLAGS_READONLY;
733 uint32_t iChain;
734 for (iChain = 0; iChain < cChain && RT_SUCCESS(rc); iChain++)
735 rc = VDOpen(pDisk, pszFormat, pElement->paArgs[iChain].psz, iChain + 1 >= cChain ? fFinal : fReadOnly, NULL);
736 }
737 if (RT_SUCCESS(rc))
738 {
739 RTVFSFILE hVfsFile;
740 rc = VDCreateVfsFileFromDisk(pDisk, VD_VFSFILE_DESTROY_ON_RELEASE, &hVfsFile);
741 if (RT_SUCCESS(rc))
742 {
743 RTStrFree(pszFormat);
744
745 *phVfsObj = RTVfsObjFromFile(hVfsFile);
746 RTVfsFileRelease(hVfsFile);
747
748 if (*phVfsObj != NIL_RTVFSOBJ)
749 return VINF_SUCCESS;
750 return VERR_VFS_CHAIN_CAST_FAILED;
751 }
752 }
753 VDDestroy(pDisk);
754 }
755 RTStrFree(pszFormat);
756 }
757 return rc;
758}
759
760
761/**
762 * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnCanReuseElement}
763 */
764static DECLCALLBACK(bool) vdVfsChain_CanReuseElement(PCRTVFSCHAINELEMENTREG pProviderReg,
765 PCRTVFSCHAINSPEC pSpec, PCRTVFSCHAINELEMSPEC pElement,
766 PCRTVFSCHAINSPEC pReuseSpec, PCRTVFSCHAINELEMSPEC pReuseElement)
767{
768 RT_NOREF(pProviderReg, pSpec, pElement, pReuseSpec, pReuseElement);
769 return false;
770}
771
772
773/** VFS chain element 'file'. */
774static RTVFSCHAINELEMENTREG g_rtVfsChainIsoFsVolReg =
775{
776 /* uVersion = */ RTVFSCHAINELEMENTREG_VERSION,
777 /* fReserved = */ 0,
778 /* pszName = */ "vd",
779 /* ListEntry = */ { NULL, NULL },
780 /* pszHelp = */ "Opens a container image using the VD API.\n"
781 "To open a snapshot chain, start with the root image and end with the more recent diff image.\n"
782 "The final argument can be a flag 'ro' or 'r' for read-only, 'rw' for read-write.",
783 /* pfnValidate = */ vdVfsChain_Validate,
784 /* pfnInstantiate = */ vdVfsChain_Instantiate,
785 /* pfnCanReuseElement = */ vdVfsChain_CanReuseElement,
786 /* uEndMarker = */ RTVFSCHAINELEMENTREG_VERSION
787};
788
789RTVFSCHAIN_AUTO_REGISTER_ELEMENT_PROVIDER(&g_rtVfsChainIsoFsVolReg, rtVfsChainIsoFsVolReg);
790
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