summaryrefslogtreecommitdiffstats
path: root/src/VBox/Storage/VDIfVfs.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Storage/VDIfVfs.cpp')
-rw-r--r--src/VBox/Storage/VDIfVfs.cpp398
1 files changed, 398 insertions, 0 deletions
diff --git a/src/VBox/Storage/VDIfVfs.cpp b/src/VBox/Storage/VDIfVfs.cpp
new file mode 100644
index 00000000..38648fde
--- /dev/null
+++ b/src/VBox/Storage/VDIfVfs.cpp
@@ -0,0 +1,398 @@
+/* $Id: VDIfVfs.cpp $ */
+/** @file
+ * Virtual Disk Image (VDI), I/O interface to IPRT VFS I/O stream glue.
+ */
+
+/*
+ * Copyright (C) 2012-2020 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+
+/*********************************************************************************************************************************
+* 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 <VBox/vd-ifs-internal.h>
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+
+/**
+ * The internal data of an VD I/O to VFS file or I/O stream wrapper.
+ */
+typedef struct VDIFVFSIOSFILE
+{
+ /** The VD I/O interface we prefer wrap.
+ * Can be NULL, in which case pVDIfsIoInt must be valid. */
+ PVDINTERFACEIO pVDIfsIo;
+ /** The VD I/O interface we alternatively can wrap.
+ Can be NULL, in which case pVDIfsIo must be valid. */
+ PVDINTERFACEIOINT pVDIfsIoInt;
+ /** User pointer to pass to the VD I/O interface methods. */
+ PVDIOSTORAGE pStorage;
+ /** The current stream position. */
+ RTFOFF offCurPos;
+} VDIFVFSIOSFILE;
+/** Pointer to a the internal data of a DVM volume file. */
+typedef VDIFVFSIOSFILE *PVDIFVFSIOSFILE;
+
+
+
+/**
+ * @interface_method_impl{RTVFSOBJOPS,pfnClose}
+ */
+static DECLCALLBACK(int) vdIfVfsIos_Close(void *pvThis)
+{
+ /* We don't close anything. */
+ RT_NOREF1(pvThis);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
+ */
+static DECLCALLBACK(int) vdIfVfsIos_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
+{
+ NOREF(pvThis);
+ NOREF(pObjInfo);
+ NOREF(enmAddAttr);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSIOSTREAMOPS,pfnRead}
+ */
+static DECLCALLBACK(int) vdIfVfsIos_Read(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbRead)
+{
+ PVDIFVFSIOSFILE pThis = (PVDIFVFSIOSFILE)pvThis;
+ Assert(pSgBuf->cSegs == 1); NOREF(fBlocking);
+ Assert(off >= -1);
+
+ /*
+ * This may end up being a little more complicated, esp. wrt VERR_EOF.
+ */
+ if (off == -1)
+ off = pThis->offCurPos;
+ int rc;
+ if (pThis->pVDIfsIo)
+ rc = vdIfIoFileReadSync(pThis->pVDIfsIo, pThis->pStorage, off, pSgBuf[0].pvSegCur, pSgBuf->paSegs[0].cbSeg, pcbRead);
+ else
+ {
+ rc = vdIfIoIntFileReadSync(pThis->pVDIfsIoInt, (PVDIOSTORAGE)pThis->pStorage, off, pSgBuf[0].pvSegCur, pSgBuf->paSegs[0].cbSeg);
+ if (pcbRead)
+ *pcbRead = RT_SUCCESS(rc) ? pSgBuf->paSegs[0].cbSeg : 0;
+ }
+ if (RT_SUCCESS(rc))
+ {
+ size_t cbAdvance = pcbRead ? *pcbRead : pSgBuf->paSegs[0].cbSeg;
+ pThis->offCurPos = off + cbAdvance;
+ if (pcbRead && !cbAdvance)
+ rc = VINF_EOF;
+ }
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSIOSTREAMOPS,pfnWrite}
+ */
+static DECLCALLBACK(int) vdIfVfsIos_Write(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbWritten)
+{
+ PVDIFVFSIOSFILE pThis = (PVDIFVFSIOSFILE)pvThis;
+ Assert(pSgBuf->cSegs == 1); NOREF(fBlocking);
+ Assert(off >= -1);
+
+ /*
+ * This may end up being a little more complicated, esp. wrt VERR_EOF.
+ */
+ if (off == -1)
+ off = pThis->offCurPos;
+ int rc;
+ if (pThis->pVDIfsIo)
+ rc = vdIfIoFileWriteSync(pThis->pVDIfsIo, pThis->pStorage, off, pSgBuf[0].pvSegCur, pSgBuf->paSegs[0].cbSeg, pcbWritten);
+ else
+ {
+ rc = vdIfIoIntFileWriteSync(pThis->pVDIfsIoInt, pThis->pStorage, off, pSgBuf[0].pvSegCur, pSgBuf->paSegs[0].cbSeg);
+ if (pcbWritten)
+ *pcbWritten = RT_SUCCESS(rc) ? pSgBuf->paSegs[0].cbSeg : 0;
+ }
+ if (RT_SUCCESS(rc))
+ pThis->offCurPos = off + (pcbWritten ? *pcbWritten : pSgBuf->paSegs[0].cbSeg);
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSIOSTREAMOPS,pfnFlush}
+ */
+static DECLCALLBACK(int) vdIfVfsIos_Flush(void *pvThis)
+{
+ PVDIFVFSIOSFILE pThis = (PVDIFVFSIOSFILE)pvThis;
+ int rc;
+ if (pThis->pVDIfsIo)
+ rc = vdIfIoFileFlushSync(pThis->pVDIfsIo, pThis->pStorage);
+ else
+ rc = vdIfIoIntFileFlushSync(pThis->pVDIfsIoInt, pThis->pStorage);
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSIOSTREAMOPS,pfnTell}
+ */
+static DECLCALLBACK(int) vdIfVfsIos_Tell(void *pvThis, PRTFOFF poffActual)
+{
+ PVDIFVFSIOSFILE pThis = (PVDIFVFSIOSFILE)pvThis;
+ *poffActual = pThis->offCurPos;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * VFS I/O stream operations for a VD file or stream.
+ */
+DECL_HIDDEN_CONST(const RTVFSIOSTREAMOPS) g_vdIfVfsIosOps =
+{
+ { /* Obj */
+ RTVFSOBJOPS_VERSION,
+ RTVFSOBJTYPE_IO_STREAM,
+ "VDIfIos",
+ vdIfVfsIos_Close,
+ vdIfVfsIos_QueryInfo,
+ RTVFSOBJOPS_VERSION
+ },
+ RTVFSIOSTREAMOPS_VERSION,
+ RTVFSIOSTREAMOPS_FEAT_NO_SG,
+ vdIfVfsIos_Read,
+ vdIfVfsIos_Write,
+ vdIfVfsIos_Flush,
+ NULL /*PollOne*/,
+ vdIfVfsIos_Tell,
+ NULL /*Skip*/,
+ NULL /*ZeroFill*/,
+ RTVFSIOSTREAMOPS_VERSION,
+
+};
+
+VBOXDDU_DECL(int) VDIfCreateVfsStream(PVDINTERFACEIO pVDIfsIo, void *pvStorage, uint32_t fFlags, PRTVFSIOSTREAM phVfsIos)
+{
+ AssertPtrReturn(pVDIfsIo, VERR_INVALID_HANDLE);
+ AssertPtrReturn(phVfsIos, VERR_INVALID_POINTER);
+
+ /*
+ * Create the volume file.
+ */
+ RTVFSIOSTREAM hVfsIos;
+ PVDIFVFSIOSFILE pThis;
+ int rc = RTVfsNewIoStream(&g_vdIfVfsIosOps, sizeof(*pThis), fFlags,
+ NIL_RTVFS, NIL_RTVFSLOCK, &hVfsIos, (void **)&pThis);
+ if (RT_SUCCESS(rc))
+ {
+ pThis->pVDIfsIo = pVDIfsIo;
+ pThis->pVDIfsIoInt = NULL;
+ pThis->pStorage = (PVDIOSTORAGE)pvStorage;
+ pThis->offCurPos = 0;
+
+ *phVfsIos = hVfsIos;
+ return VINF_SUCCESS;
+ }
+
+ return rc;
+}
+
+
+
+/**
+ * @interface_method_impl{RTVFSOBJSETOPS,pfnSetMode}
+ */
+static DECLCALLBACK(int) vdIfVfsFile_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) vdIfVfsFile_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) vdIfVfsFile_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) vdIfVfsFile_Seek(void *pvThis, RTFOFF offSeek, unsigned uMethod, PRTFOFF poffActual)
+{
+ PVDIFVFSIOSFILE pThis = (PVDIFVFSIOSFILE)pvThis;
+
+ uint64_t cbFile;
+ int rc;
+ if (pThis->pVDIfsIo)
+ rc = vdIfIoFileGetSize(pThis->pVDIfsIo, pThis->pStorage, &cbFile);
+ else
+ rc = vdIfIoIntFileGetSize(pThis->pVDIfsIoInt, pThis->pStorage, &cbFile);
+ if (RT_FAILURE(rc))
+ return rc;
+ if (cbFile >= (uint64_t)RTFOFF_MAX)
+ cbFile = RTFOFF_MAX;
+
+ /* Recalculate the request to RTFILE_SEEK_BEGIN. */
+ switch (uMethod)
+ {
+ case RTFILE_SEEK_BEGIN:
+ break;
+ case RTFILE_SEEK_CURRENT:
+ offSeek += pThis->offCurPos;
+ break;
+ case RTFILE_SEEK_END:
+ offSeek = cbFile + offSeek;
+ break;
+ default:
+ AssertFailedReturn(VERR_INVALID_PARAMETER);
+ }
+
+ /* Do limit checks. */
+ if (offSeek < 0)
+ offSeek = 0;
+ else if (offSeek > (RTFOFF)cbFile)
+ offSeek = cbFile;
+
+ /* Apply and return. */
+ pThis->offCurPos = offSeek;
+ if (poffActual)
+ *poffActual = offSeek;
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSFILEOPS,pfnQuerySize}
+ */
+static DECLCALLBACK(int) vdIfVfsFile_QuerySize(void *pvThis, uint64_t *pcbFile)
+{
+ PVDIFVFSIOSFILE pThis = (PVDIFVFSIOSFILE)pvThis;
+ int rc;
+ if (pThis->pVDIfsIo)
+ rc = vdIfIoFileGetSize(pThis->pVDIfsIo, pThis->pStorage, pcbFile);
+ else
+ rc = vdIfIoIntFileGetSize(pThis->pVDIfsIoInt, pThis->pStorage, pcbFile);
+ return rc;
+}
+
+
+
+/**
+ * VFS file operations for a VD file.
+ */
+DECL_HIDDEN_CONST(const RTVFSFILEOPS) g_vdIfVfsFileOps =
+{
+ { /* I/O stream */
+ { /* Obj */
+ RTVFSOBJOPS_VERSION,
+ RTVFSOBJTYPE_FILE,
+ "VDIfFile",
+ vdIfVfsIos_Close,
+ vdIfVfsIos_QueryInfo,
+ RTVFSOBJOPS_VERSION
+ },
+ RTVFSIOSTREAMOPS_VERSION,
+ RTVFSIOSTREAMOPS_FEAT_NO_SG,
+ vdIfVfsIos_Read,
+ vdIfVfsIos_Write,
+ vdIfVfsIos_Flush,
+ NULL /*PollOne*/,
+ vdIfVfsIos_Tell,
+ NULL /*Skip*/,
+ NULL /*ZeroFill*/,
+ RTVFSIOSTREAMOPS_VERSION,
+ },
+ RTVFSFILEOPS_VERSION,
+ 0,
+ { /* ObjSet */
+ RTVFSOBJSETOPS_VERSION,
+ RT_UOFFSETOF(RTVFSFILEOPS, ObjSet) - RT_UOFFSETOF(RTVFSFILEOPS, Stream.Obj),
+ vdIfVfsFile_SetMode,
+ vdIfVfsFile_SetTimes,
+ vdIfVfsFile_SetOwner,
+ RTVFSOBJSETOPS_VERSION
+ },
+ vdIfVfsFile_Seek,
+ vdIfVfsFile_QuerySize,
+ NULL /*SetSize*/,
+ NULL /*QueryMaxSize*/,
+ RTVFSFILEOPS_VERSION,
+};
+
+
+VBOXDDU_DECL(int) VDIfCreateVfsFile(PVDINTERFACEIO pVDIfs, struct VDINTERFACEIOINT *pVDIfsInt, void *pvStorage, uint32_t fFlags, PRTVFSFILE phVfsFile)
+{
+ AssertReturn((pVDIfs != NULL) != (pVDIfsInt != NULL), VERR_INVALID_PARAMETER); /* Exactly one needs to be specified. */
+ AssertPtrReturn(phVfsFile, VERR_INVALID_POINTER);
+
+ /*
+ * Create the volume file.
+ */
+ RTVFSFILE hVfsFile;
+ PVDIFVFSIOSFILE pThis;
+ int rc = RTVfsNewFile(&g_vdIfVfsFileOps, sizeof(*pThis), fFlags,
+ NIL_RTVFS, NIL_RTVFSLOCK, &hVfsFile, (void **)&pThis);
+ if (RT_SUCCESS(rc))
+ {
+ pThis->pVDIfsIo = pVDIfs;
+ pThis->pVDIfsIoInt = pVDIfsInt;
+ pThis->pStorage = (PVDIOSTORAGE)pvStorage;
+ pThis->offCurPos = 0;
+
+ *phVfsFile = hVfsFile;
+ return VINF_SUCCESS;
+ }
+
+ return rc;
+}
+