diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 16:49:04 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 16:49:04 +0000 |
commit | 16f504a9dca3fe3b70568f67b7d41241ae485288 (patch) | |
tree | c60f36ada0496ba928b7161059ba5ab1ab224f9d /src/VBox/Storage/VDVfs.cpp | |
parent | Initial commit. (diff) | |
download | virtualbox-16f504a9dca3fe3b70568f67b7d41241ae485288.tar.xz virtualbox-16f504a9dca3fe3b70568f67b7d41241ae485288.zip |
Adding upstream version 7.0.6-dfsg.upstream/7.0.6-dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/Storage/VDVfs.cpp')
-rw-r--r-- | src/VBox/Storage/VDVfs.cpp | 800 |
1 files changed, 800 insertions, 0 deletions
diff --git a/src/VBox/Storage/VDVfs.cpp b/src/VBox/Storage/VDVfs.cpp new file mode 100644 index 00000000..7a728b1b --- /dev/null +++ b/src/VBox/Storage/VDVfs.cpp @@ -0,0 +1,800 @@ +/* $Id: VDVfs.cpp $ */ +/** @file + * Virtual Disk Container implementation. - VFS glue. + */ + +/* + * Copyright (C) 2012-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * SPDX-License-Identifier: GPL-3.0-only + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <iprt/types.h> +#include <iprt/assert.h> +#include <iprt/mem.h> +#include <iprt/err.h> +#include <iprt/asm.h> +#include <iprt/string.h> +#include <iprt/file.h> +#include <iprt/sg.h> +#include <iprt/vfslowlevel.h> +#include <iprt/poll.h> +#include <VBox/vd.h> + +#include "VDInternal.h" + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ + +/** + * The internal data of a DVM volume I/O stream. + */ +typedef struct VDVFSFILE +{ + /** The volume the VFS file belongs to. */ + PVDISK pDisk; + /** Current position. */ + uint64_t offCurPos; + /** Flags given during creation. */ + uint32_t fFlags; +} VDVFSFILE; +/** Pointer to a the internal data of a DVM volume file. */ +typedef VDVFSFILE *PVDVFSFILE; + +/** + * VD read helper taking care of unaligned accesses. + * + * @return VBox status code. + * @param pDisk VD disk container. + * @param off Offset to start reading from. + * @param pvBuf Pointer to the buffer to read into. + * @param cbRead Amount of bytes to read. + */ +static int vdReadHelper(PVDISK pDisk, uint64_t off, void *pvBuf, size_t cbRead) +{ + int rc; + + /* Take direct route if the request is sector aligned. */ + uint64_t const offMisalign = off & 511; + size_t const cbMisalign = (off + cbRead) & 511; + if ( !offMisalign + && !cbMisalign) + rc = VDRead(pDisk, off, pvBuf, cbRead); + else + { + uint8_t *pbBuf = (uint8_t *)pvBuf; + uint8_t abBuf[512]; + + /* Unaligned buffered read of head. Aligns the offset. */ + if (offMisalign) + { + rc = VDRead(pDisk, off - offMisalign, abBuf, 512); + if (RT_SUCCESS(rc)) + { + size_t const cbPart = RT_MIN(512 - offMisalign, cbRead); + memcpy(pbBuf, &abBuf[offMisalign], cbPart); + pbBuf += cbPart; + off += cbPart; + cbRead -= cbPart; + } + } + else + rc = VINF_SUCCESS; + + /* Aligned direct read. */ + if ( RT_SUCCESS(rc) + && cbRead >= 512) + { + Assert(!(off % 512)); + + size_t cbPart = cbRead - cbMisalign; + Assert(!(cbPart % 512)); + rc = VDRead(pDisk, off, pbBuf, cbPart); + if (RT_SUCCESS(rc)) + { + pbBuf += cbPart; + off += cbPart; + cbRead -= cbPart; + } + } + + /* Unaligned buffered read of tail. */ + if ( RT_SUCCESS(rc) + && cbRead) + { + Assert(cbRead == cbMisalign); + Assert(cbRead < 512); + Assert(!(off % 512)); + + rc = VDRead(pDisk, off, abBuf, 512); + if (RT_SUCCESS(rc)) + memcpy(pbBuf, abBuf, cbRead); + } + } + + return rc; +} + + +/** + * VD write helper taking care of unaligned accesses. + * + * @return VBox status code. + * @param pDisk VD disk container. + * @param off Offset to start writing to. + * @param pvSrc Pointer to the buffer to read from. + * @param cbWrite Amount of bytes to write. + */ +static int vdWriteHelper(PVDISK pDisk, uint64_t off, const void *pvSrc, size_t cbWrite) +{ + uint8_t const *pbSrc = (uint8_t const *)pvSrc; + uint8_t abBuf[4096]; + int rc; + + /* + * Take direct route if the request is sector aligned. + */ + uint64_t const offMisalign = off & 511; + size_t const cbMisalign = (off + cbWrite) & 511; + if ( !offMisalign + && !cbMisalign) + { + if (RTListIsEmpty(&pDisk->ListFilterChainWrite)) + rc = VDWrite(pDisk, off, pbSrc, cbWrite); + else + { + /* Filtered writes must be double buffered as the filter may need to modify the input buffer directly. */ + do + { + size_t cbThisWrite = RT_MIN(cbWrite, sizeof(abBuf)); + rc = VDWrite(pDisk, off, memcpy(abBuf, pbSrc, cbThisWrite), cbThisWrite); + if (RT_SUCCESS(rc)) + { + pbSrc += cbThisWrite; + off += cbThisWrite; + cbWrite -= cbThisWrite; + } + else + break; + } while (cbWrite > 0); + } + } + else + { + + /* + * Unaligned buffered read+write of head. Aligns the offset. + */ + if (offMisalign) + { + rc = VDRead(pDisk, off - offMisalign, abBuf, 512); + if (RT_SUCCESS(rc)) + { + size_t const cbPart = RT_MIN(512 - offMisalign, cbWrite); + memcpy(&abBuf[offMisalign], pbSrc, cbPart); + rc = VDWrite(pDisk, off - offMisalign, abBuf, 512); + if (RT_SUCCESS(rc)) + { + pbSrc += cbPart; + off += cbPart; + cbWrite -= cbPart; + } + } + } + else + rc = VINF_SUCCESS; + + /* + * Aligned direct write. + */ + if ( RT_SUCCESS(rc) + && cbWrite >= 512) + { + Assert(!(off % 512)); + size_t cbPart = cbWrite - cbMisalign; + Assert(!(cbPart % 512)); + + if (RTListIsEmpty(&pDisk->ListFilterChainWrite)) + { + rc = VDWrite(pDisk, off, pbSrc, cbPart); + if (RT_SUCCESS(rc)) + { + pbSrc += cbPart; + off += cbPart; + cbWrite -= cbPart; + } + } + else + { + /* Filtered writes must be double buffered as the filter may need to modify the input buffer directly. */ + do + { + size_t cbThisWrite = RT_MIN(cbPart, sizeof(abBuf)); + rc = VDWrite(pDisk, off, memcpy(abBuf, pbSrc, cbThisWrite), cbThisWrite); + if (RT_SUCCESS(rc)) + { + pbSrc += cbThisWrite; + off += cbThisWrite; + cbWrite -= cbThisWrite; + cbPart -= cbThisWrite; + } + else + break; + } while (cbPart > 0); + } + } + + /* + * Unaligned buffered read+write of tail. + */ + if ( RT_SUCCESS(rc) + && cbWrite > 0) + { + Assert(cbWrite == cbMisalign); + Assert(cbWrite < 512); + Assert(!(off % 512)); + + rc = VDRead(pDisk, off, abBuf, 512); + if (RT_SUCCESS(rc)) + { + memcpy(abBuf, pbSrc, cbWrite); + rc = VDWrite(pDisk, off, abBuf, 512); + } + } + } + + return rc; +} + + +/** + * @interface_method_impl{RTVFSOBJOPS,pfnClose} + */ +static DECLCALLBACK(int) vdVfsFile_Close(void *pvThis) +{ + PVDVFSFILE pThis = (PVDVFSFILE)pvThis; + + if (pThis->fFlags & VD_VFSFILE_DESTROY_ON_RELEASE) + VDDestroy(pThis->pDisk); + + return VINF_SUCCESS; +} + + +/** + * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo} + */ +static DECLCALLBACK(int) vdVfsFile_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr) +{ + PVDVFSFILE pThis = (PVDVFSFILE)pvThis; + unsigned const cOpenImages = VDGetCount(pThis->pDisk); + + pObjInfo->cbObject = VDGetSize(pThis->pDisk, cOpenImages - 1); + pObjInfo->cbAllocated = 0; + for (unsigned iImage = 0; iImage < cOpenImages; iImage++) + pObjInfo->cbAllocated += VDGetFileSize(pThis->pDisk, iImage); + + /** @todo enumerate the disk images directly... */ + RTTimeNow(&pObjInfo->AccessTime); + pObjInfo->BirthTime = pObjInfo->AccessTime; + pObjInfo->ChangeTime = pObjInfo->AccessTime; + pObjInfo->ModificationTime = pObjInfo->AccessTime; + + pObjInfo->Attr.fMode = RTFS_DOS_NT_NORMAL | RTFS_TYPE_FILE | 0644; + pObjInfo->Attr.enmAdditional = enmAddAttr; + switch (enmAddAttr) + { + case RTFSOBJATTRADD_UNIX: + pObjInfo->Attr.u.Unix.uid = NIL_RTUID; + pObjInfo->Attr.u.Unix.gid = NIL_RTGID; + pObjInfo->Attr.u.Unix.cHardlinks = 1; + pObjInfo->Attr.u.Unix.INodeIdDevice = 0; + pObjInfo->Attr.u.Unix.INodeId = 0; + pObjInfo->Attr.u.Unix.fFlags = 0; + pObjInfo->Attr.u.Unix.GenerationId = 0; + pObjInfo->Attr.u.Unix.Device = 0; + break; + + case RTFSOBJATTRADD_UNIX_OWNER: + pObjInfo->Attr.u.UnixOwner.uid = NIL_RTUID; + pObjInfo->Attr.u.UnixOwner.szName[0] = '\0'; + break; + case RTFSOBJATTRADD_UNIX_GROUP: + pObjInfo->Attr.u.UnixGroup.gid = NIL_RTGID; + pObjInfo->Attr.u.UnixGroup.szName[0] = '\0'; + break; + case RTFSOBJATTRADD_EASIZE: + pObjInfo->Attr.u.EASize.cb = 0; + break; + + default: + AssertFailedReturn(VERR_INVALID_PARAMETER); + } + + return VINF_SUCCESS; +} + + +/** + * @interface_method_impl{RTVFSIOSTREAMOPS,pfnRead} + */ +static DECLCALLBACK(int) vdVfsFile_Read(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbRead) +{ + PVDVFSFILE pThis = (PVDVFSFILE)pvThis; + + Assert(pSgBuf->cSegs == 1); + NOREF(fBlocking); + + /* + * Find the current position and check if it's within the volume. + */ + uint64_t offUnsigned = off < 0 ? pThis->offCurPos : (uint64_t)off; + uint64_t const cbImage = VDGetSize(pThis->pDisk, VD_LAST_IMAGE); + if (offUnsigned >= cbImage) + { + if (pcbRead) + { + *pcbRead = 0; + pThis->offCurPos = cbImage; + return VINF_EOF; + } + return VERR_EOF; + } + + int rc = VINF_SUCCESS; + size_t cbLeftToRead = pSgBuf->paSegs[0].cbSeg; + if (offUnsigned + cbLeftToRead <= cbImage) + { + if (pcbRead) + *pcbRead = cbLeftToRead; + } + else + { + if (!pcbRead) + return VERR_EOF; + *pcbRead = cbLeftToRead = (size_t)(cbImage - offUnsigned); + rc = VINF_EOF; + } + + /* + * Ok, we've got a valid stretch within the file. Do the reading. + */ + if (cbLeftToRead > 0) + { + int rc2 = vdReadHelper(pThis->pDisk, offUnsigned, pSgBuf->paSegs[0].pvSeg, cbLeftToRead); + if (RT_SUCCESS(rc2)) + offUnsigned += cbLeftToRead; + else + rc = rc2; + } + + pThis->offCurPos = offUnsigned; + return rc; +} + + +/** + * @interface_method_impl{RTVFSIOSTREAMOPS,pfnWrite} + */ +static DECLCALLBACK(int) vdVfsFile_Write(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbWritten) +{ + PVDVFSFILE pThis = (PVDVFSFILE)pvThis; + + Assert(pSgBuf->cSegs == 1); + NOREF(fBlocking); + + /* + * Find the current position and check if it's within the volume. + * Writing beyond the end of a volume is not supported. + */ + uint64_t offUnsigned = off < 0 ? pThis->offCurPos : (uint64_t)off; + uint64_t const cbImage = VDGetSize(pThis->pDisk, VD_LAST_IMAGE); + if (offUnsigned >= cbImage) + { + if (pcbWritten) + { + *pcbWritten = 0; + pThis->offCurPos = cbImage; + } + return VERR_EOF; + } + + size_t cbLeftToWrite; + if (offUnsigned + pSgBuf->paSegs[0].cbSeg <= cbImage) + { + cbLeftToWrite = pSgBuf->paSegs[0].cbSeg; + if (pcbWritten) + *pcbWritten = cbLeftToWrite; + } + else + { + if (!pcbWritten) + return VERR_EOF; + *pcbWritten = cbLeftToWrite = (size_t)(cbImage - offUnsigned); + } + + /* + * Ok, we've got a valid stretch within the file. Do the reading. + */ + int rc = VINF_SUCCESS; + if (cbLeftToWrite > 0) + { + rc = vdWriteHelper(pThis->pDisk, offUnsigned, pSgBuf->paSegs[0].pvSeg, cbLeftToWrite); + if (RT_SUCCESS(rc)) + offUnsigned += cbLeftToWrite; + } + + pThis->offCurPos = offUnsigned; + return rc; +} + + +/** + * @interface_method_impl{RTVFSIOSTREAMOPS,pfnFlush} + */ +static DECLCALLBACK(int) vdVfsFile_Flush(void *pvThis) +{ + PVDVFSFILE pThis = (PVDVFSFILE)pvThis; + return VDFlush(pThis->pDisk); +} + + +/** + * @interface_method_impl{RTVFSIOSTREAMOPS,pfnTell} + */ +static DECLCALLBACK(int) vdVfsFile_Tell(void *pvThis, PRTFOFF poffActual) +{ + PVDVFSFILE pThis = (PVDVFSFILE)pvThis; + *poffActual = pThis->offCurPos; + return VINF_SUCCESS; +} + + +/** + * @interface_method_impl{RTVFSOBJSETOPS,pfnSetMode} + */ +static DECLCALLBACK(int) vdVfsFile_SetMode(void *pvThis, RTFMODE fMode, RTFMODE fMask) +{ + NOREF(pvThis); + NOREF(fMode); + NOREF(fMask); + return VERR_NOT_SUPPORTED; +} + + +/** + * @interface_method_impl{RTVFSOBJSETOPS,pfnSetTimes} + */ +static DECLCALLBACK(int) vdVfsFile_SetTimes(void *pvThis, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime, + PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime) +{ + NOREF(pvThis); + NOREF(pAccessTime); + NOREF(pModificationTime); + NOREF(pChangeTime); + NOREF(pBirthTime); + return VERR_NOT_SUPPORTED; +} + + +/** + * @interface_method_impl{RTVFSOBJSETOPS,pfnSetOwner} + */ +static DECLCALLBACK(int) vdVfsFile_SetOwner(void *pvThis, RTUID uid, RTGID gid) +{ + NOREF(pvThis); + NOREF(uid); + NOREF(gid); + return VERR_NOT_SUPPORTED; +} + + +/** + * @interface_method_impl{RTVFSFILEOPS,pfnSeek} + */ +static DECLCALLBACK(int) vdVfsFile_Seek(void *pvThis, RTFOFF offSeek, unsigned uMethod, PRTFOFF poffActual) +{ + PVDVFSFILE pThis = (PVDVFSFILE)pvThis; + + /* + * Seek relative to which position. + */ + uint64_t offWrt; + switch (uMethod) + { + case RTFILE_SEEK_BEGIN: + offWrt = 0; + break; + + case RTFILE_SEEK_CURRENT: + offWrt = pThis->offCurPos; + break; + + case RTFILE_SEEK_END: + offWrt = VDGetSize(pThis->pDisk, VD_LAST_IMAGE); + break; + + default: + return VERR_INTERNAL_ERROR_5; + } + + /* + * Calc new position, take care to stay without bounds. + */ + uint64_t offNew; + if (offSeek == 0) + offNew = offWrt; + else if (offSeek > 0) + { + offNew = offWrt + offSeek; + if ( offNew < offWrt + || offNew > RTFOFF_MAX) + offNew = RTFOFF_MAX; + } + else if ((uint64_t)-offSeek < offWrt) + offNew = offWrt + offSeek; + else + offNew = 0; + + /* + * Update the state and set return value. + */ + pThis->offCurPos = offNew; + + *poffActual = offNew; + return VINF_SUCCESS; +} + + +/** + * @interface_method_impl{RTVFSFILEOPS,pfnQuerySize} + */ +static DECLCALLBACK(int) vdVfsFile_QuerySize(void *pvThis, uint64_t *pcbFile) +{ + PVDVFSFILE pThis = (PVDVFSFILE)pvThis; + *pcbFile = VDGetSize(pThis->pDisk, VD_LAST_IMAGE); + return VINF_SUCCESS; +} + + +/** + * Standard file operations. + */ +DECL_HIDDEN_CONST(const RTVFSFILEOPS) g_vdVfsStdFileOps = +{ + { /* Stream */ + { /* Obj */ + RTVFSOBJOPS_VERSION, + RTVFSOBJTYPE_FILE, + "VDFile", + vdVfsFile_Close, + vdVfsFile_QueryInfo, + NULL, + RTVFSOBJOPS_VERSION + }, + RTVFSIOSTREAMOPS_VERSION, + RTVFSIOSTREAMOPS_FEAT_NO_SG, + vdVfsFile_Read, + vdVfsFile_Write, + vdVfsFile_Flush, + NULL /*PollOne*/, + vdVfsFile_Tell, + NULL /*Skip*/, + NULL /*ZeroFill*/, + RTVFSIOSTREAMOPS_VERSION, + }, + RTVFSFILEOPS_VERSION, + /*RTVFSIOFILEOPS_FEAT_NO_AT_OFFSET*/ 0, + { /* ObjSet */ + RTVFSOBJSETOPS_VERSION, + RT_UOFFSETOF(RTVFSFILEOPS, ObjSet) - RT_UOFFSETOF(RTVFSFILEOPS, Stream.Obj), + vdVfsFile_SetMode, + vdVfsFile_SetTimes, + vdVfsFile_SetOwner, + RTVFSOBJSETOPS_VERSION + }, + vdVfsFile_Seek, + vdVfsFile_QuerySize, + NULL /*SetSize*/, + NULL /*QueryMaxSize*/, + RTVFSFILEOPS_VERSION +}; + + +VBOXDDU_DECL(int) VDCreateVfsFileFromDisk(PVDISK pDisk, uint32_t fFlags, + PRTVFSFILE phVfsFile) +{ + AssertPtrReturn(pDisk, VERR_INVALID_HANDLE); + AssertPtrReturn(phVfsFile, VERR_INVALID_POINTER); + AssertReturn((fFlags & ~VD_VFSFILE_FLAGS_MASK) == 0, VERR_INVALID_PARAMETER); + + /* + * Create the volume file. + */ + RTVFSFILE hVfsFile; + PVDVFSFILE pThis; + int rc = RTVfsNewFile(&g_vdVfsStdFileOps, sizeof(*pThis), RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_WRITE, + NIL_RTVFS, NIL_RTVFSLOCK, &hVfsFile, (void **)&pThis); + if (RT_SUCCESS(rc)) + { + pThis->offCurPos = 0; + pThis->pDisk = pDisk; + pThis->fFlags = fFlags; + + *phVfsFile = hVfsFile; + return VINF_SUCCESS; + } + + return rc; +} + + +/** + * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnValidate} + */ +static DECLCALLBACK(int) vdVfsChain_Validate(PCRTVFSCHAINELEMENTREG pProviderReg, PRTVFSCHAINSPEC pSpec, + PRTVFSCHAINELEMSPEC pElement, uint32_t *poffError, PRTERRINFO pErrInfo) +{ + RT_NOREF(pProviderReg, pSpec); + + /* + * Basic checks. + */ + if (pElement->enmTypeIn != RTVFSOBJTYPE_INVALID) + return VERR_VFS_CHAIN_MUST_BE_FIRST_ELEMENT; + if ( pElement->enmType != RTVFSOBJTYPE_FILE + && pElement->enmType != RTVFSOBJTYPE_IO_STREAM) + return VERR_VFS_CHAIN_ONLY_FILE_OR_IOS; + + if (pElement->cArgs < 1) + return VERR_VFS_CHAIN_AT_LEAST_ONE_ARG; + + /* + * Parse the flag if present, save in pElement->uProvider. + */ + uint32_t fFlags = (pSpec->fOpenFile & RTFILE_O_ACCESS_MASK) == RTFILE_O_READ + ? VD_OPEN_FLAGS_READONLY : VD_OPEN_FLAGS_NORMAL; + if (pElement->cArgs > 1) + { + pElement->paArgs[pElement->cArgs - 1].uProvider = true; /* indicates flags */ + const char *psz = pElement->paArgs[pElement->cArgs - 1].psz; + if (*psz) + { + if ( !strcmp(psz, "ro") + || !strcmp(psz, "r")) + { + fFlags &= ~(VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_NORMAL); + fFlags |= VD_OPEN_FLAGS_READONLY; + } + else if (!strcmp(psz, "rw")) + { + fFlags &= ~(VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_NORMAL); + fFlags |= VD_OPEN_FLAGS_NORMAL; + } + else if (strlen(psz) <= 4) + { + *poffError = pElement->paArgs[pElement->cArgs - 1].offSpec; + return RTErrInfoSet(pErrInfo, VERR_VFS_CHAIN_INVALID_ARGUMENT, "Expected 'ro' or 'rw' as argument"); + } + else + pElement->paArgs[pElement->cArgs - 1].uProvider = false; /* indicates no flags */ + } + } + + pElement->uProvider = fFlags; + if ( pElement->cArgs > 2 + || (pElement->cArgs == 2 && !pElement->paArgs[pElement->cArgs - 1].uProvider)) + pElement->uProvider |= RT_BIT_64(63); + return VINF_SUCCESS; +} + + +/** + * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnInstantiate} + */ +static DECLCALLBACK(int) vdVfsChain_Instantiate(PCRTVFSCHAINELEMENTREG pProviderReg, PCRTVFSCHAINSPEC pSpec, + PCRTVFSCHAINELEMSPEC pElement, RTVFSOBJ hPrevVfsObj, + PRTVFSOBJ phVfsObj, uint32_t *poffError, PRTERRINFO pErrInfo) +{ + RT_NOREF(pProviderReg, pSpec, poffError, pErrInfo); + AssertReturn(hPrevVfsObj == NIL_RTVFSOBJ, VERR_VFS_CHAIN_IPE); + + /* Determin the format. */ + char *pszFormat = NULL; + VDTYPE enmType = VDTYPE_INVALID; + int rc = VDGetFormat(NULL, NULL, pElement->paArgs[0].psz, VDTYPE_INVALID, &pszFormat, &enmType); + if (RT_SUCCESS(rc)) + { + PVDISK pDisk = NULL; + rc = VDCreate(NULL, enmType, &pDisk); + if (RT_SUCCESS(rc)) + { + if (!(pElement->uProvider & RT_BIT_64(63))) + rc = VDOpen(pDisk, pszFormat, pElement->paArgs[0].psz, (uint32_t)pElement->uProvider, NULL); + else + { + uint32_t cChain = pElement->cArgs; + if (pElement->cArgs >= 2 && pElement->paArgs[pElement->cArgs - 1].uProvider != 0) + cChain--; + uint32_t const fFinal = (uint32_t)pElement->uProvider; + uint32_t const fReadOnly = (fFinal & ~(VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_NORMAL)) | VD_OPEN_FLAGS_READONLY; + uint32_t iChain; + for (iChain = 0; iChain < cChain && RT_SUCCESS(rc); iChain++) + rc = VDOpen(pDisk, pszFormat, pElement->paArgs[iChain].psz, iChain + 1 >= cChain ? fFinal : fReadOnly, NULL); + } + if (RT_SUCCESS(rc)) + { + RTVFSFILE hVfsFile; + rc = VDCreateVfsFileFromDisk(pDisk, VD_VFSFILE_DESTROY_ON_RELEASE, &hVfsFile); + if (RT_SUCCESS(rc)) + { + RTStrFree(pszFormat); + + *phVfsObj = RTVfsObjFromFile(hVfsFile); + RTVfsFileRelease(hVfsFile); + + if (*phVfsObj != NIL_RTVFSOBJ) + return VINF_SUCCESS; + return VERR_VFS_CHAIN_CAST_FAILED; + } + } + VDDestroy(pDisk); + } + RTStrFree(pszFormat); + } + return rc; +} + + +/** + * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnCanReuseElement} + */ +static DECLCALLBACK(bool) vdVfsChain_CanReuseElement(PCRTVFSCHAINELEMENTREG pProviderReg, + PCRTVFSCHAINSPEC pSpec, PCRTVFSCHAINELEMSPEC pElement, + PCRTVFSCHAINSPEC pReuseSpec, PCRTVFSCHAINELEMSPEC pReuseElement) +{ + RT_NOREF(pProviderReg, pSpec, pElement, pReuseSpec, pReuseElement); + return false; +} + + +/** VFS chain element 'file'. */ +static RTVFSCHAINELEMENTREG g_rtVfsChainIsoFsVolReg = +{ + /* uVersion = */ RTVFSCHAINELEMENTREG_VERSION, + /* fReserved = */ 0, + /* pszName = */ "vd", + /* ListEntry = */ { NULL, NULL }, + /* pszHelp = */ "Opens a container image using the VD API.\n" + "To open a snapshot chain, start with the root image and end with the more recent diff image.\n" + "The final argument can be a flag 'ro' or 'r' for read-only, 'rw' for read-write.", + /* pfnValidate = */ vdVfsChain_Validate, + /* pfnInstantiate = */ vdVfsChain_Instantiate, + /* pfnCanReuseElement = */ vdVfsChain_CanReuseElement, + /* uEndMarker = */ RTVFSCHAINELEMENTREG_VERSION +}; + +RTVFSCHAIN_AUTO_REGISTER_ELEMENT_PROVIDER(&g_rtVfsChainIsoFsVolReg, rtVfsChainIsoFsVolReg); + |