summaryrefslogtreecommitdiffstats
path: root/src/VBox/Runtime/common/dvm/dvmvfs.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Runtime/common/dvm/dvmvfs.cpp')
-rw-r--r--src/VBox/Runtime/common/dvm/dvmvfs.cpp1467
1 files changed, 1467 insertions, 0 deletions
diff --git a/src/VBox/Runtime/common/dvm/dvmvfs.cpp b/src/VBox/Runtime/common/dvm/dvmvfs.cpp
new file mode 100644
index 00000000..f22f02f9
--- /dev/null
+++ b/src/VBox/Runtime/common/dvm/dvmvfs.cpp
@@ -0,0 +1,1467 @@
+/* $Id: dvmvfs.cpp $ */
+/** @file
+ * IPRT Disk Volume Management API (DVM) - VFS glue.
+ */
+
+/*
+ * Copyright (C) 2012-2023 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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_FS /** @todo fix log group */
+#include <iprt/types.h>
+#include <iprt/assert.h>
+#include <iprt/mem.h>
+#include <iprt/dvm.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 <iprt/log.h>
+#include "internal/dvm.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/** Pointer to a volume manager VFS. */
+typedef struct RTDVMVFSVOL *PRTDVMVFSVOL;
+
+/**
+ * The internal data of a DVM volume I/O stream.
+ */
+typedef struct RTVFSDVMFILE
+{
+ /** The volume the VFS file belongs to. */
+ RTDVMVOLUME hVol;
+ /** Pointer to the VFS volume. Can be NULL. */
+ PRTDVMVFSVOL pVfsVol;
+ /** Current position. */
+ uint64_t offCurPos;
+ /** Set if readable. */
+ bool fCanRead;
+ /** Set if writable. */
+ bool fCanWrite;
+} RTVFSDVMFILE;
+/** Pointer to a the internal data of a DVM volume file. */
+typedef RTVFSDVMFILE *PRTVFSDVMFILE;
+
+/**
+ * The internal data of a DVM volume symlink.
+ */
+typedef struct RTVFSDVMSYMLINK
+{
+ /** The DVM volume the symlink represent. */
+ RTDVMVOLUME hVol;
+ /** The DVM volume manager @a hVol belongs to. */
+ RTDVM hVolMgr;
+ /** The symlink name. */
+ char *pszSymlink;
+ /** The symlink target (volXX). */
+ char szTarget[16];
+} RTVFSDVMSYMLINK;
+/** Pointer to a the internal data of a DVM volume file. */
+typedef RTVFSDVMSYMLINK *PRTVFSDVMSYMLINK;
+
+/**
+ * The volume manager VFS (root) dir data.
+ */
+typedef struct RTDVMVFSDIR
+{
+ /** Pointer to the VFS volume. */
+ PRTDVMVFSVOL pVfsVol;
+ /** The current directory offset. */
+ uint32_t offDir;
+ /** Set if we need to try return hCurVolume again because of buffer overflow. */
+ bool fReturnCurrent;
+ /** Pointer to name alias string (returned by RTDvmVolumeQueryName, free it). */
+ char *pszNameAlias;
+ /** The current DVM volume. */
+ RTDVMVOLUME hCurVolume;
+} RTDVMVFSDIR;
+/** Pointer to a volume manager VFS (root) dir. */
+typedef RTDVMVFSDIR *PRTDVMVFSDIR;
+
+/**
+ * A volume manager VFS for use in chains (thing pseudo/devfs).
+ */
+typedef struct RTDVMVFSVOL
+{
+ /** The volume manager. */
+ RTDVM hVolMgr;
+ /** Whether to close it on success. */
+ bool fCloseDvm;
+ /** Whether the access is read-only. */
+ bool fReadOnly;
+ /** Number of volumes. */
+ uint32_t cVolumes;
+ /** Self reference. */
+ RTVFS hVfsSelf;
+} RTDVMVFSVOL;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static DECLCALLBACK(int) rtDvmVfsVol_OpenRoot(void *pvThis, PRTVFSDIR phVfsDir);
+
+
+/**
+ * @interface_method_impl{RTVFSOBJOPS,pfnClose}
+ */
+static DECLCALLBACK(int) rtDvmVfsFile_Close(void *pvThis)
+{
+ PRTVFSDVMFILE pThis = (PRTVFSDVMFILE)pvThis;
+
+ RTDvmVolumeRelease(pThis->hVol);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Worker for rtDvmVfsFile_QueryInfoWorker and rtDvmVfsSym_QueryInfoWorker.
+ */
+static int rtDvmVfsFileSym_QueryAddAttrWorker(RTDVMVOLUME hVolume, RTDVM hVolMgr,
+ PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
+{
+ switch (enmAddAttr)
+ {
+ case RTFSOBJATTRADD_NOTHING:
+ case RTFSOBJATTRADD_UNIX:
+ pObjInfo->Attr.u.Unix.uid = (RTUID)RTDvmVolumeGetType(hVolume);
+ pObjInfo->Attr.u.Unix.gid = hVolMgr != NIL_RTDVM ? (RTGID)RTDvmMapGetFormatType(hVolMgr) : 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:
+ {
+ RTDVMVOLTYPE enmType = RTDvmVolumeGetType(hVolume);
+ pObjInfo->Attr.u.UnixOwner.uid = (RTUID)enmType;
+ RTStrCopy(pObjInfo->Attr.u.UnixOwner.szName, sizeof(pObjInfo->Attr.u.UnixOwner.szName),
+ RTDvmVolumeTypeGetDescr(enmType));
+ break;
+ }
+
+ case RTFSOBJATTRADD_UNIX_GROUP:
+ if (hVolMgr != NIL_RTDVM)
+ {
+ pObjInfo->Attr.u.UnixGroup.gid = (RTGID)RTDvmMapGetFormatType(hVolMgr);
+ RTStrCopy(pObjInfo->Attr.u.UnixGroup.szName, sizeof(pObjInfo->Attr.u.UnixGroup.szName),
+ RTDvmMapGetFormatName(hVolMgr));
+ }
+ else
+ {
+ 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:
+ return VERR_INVALID_PARAMETER;
+ }
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Worker for rtDvmVfsFile_QueryInfo, rtDvmVfsDir_QueryEntryInfo, and
+ * rtDvmVfsDir_ReadDir.
+ */
+static int rtDvmVfsFile_QueryInfoWorker(RTDVMVOLUME hVolume, RTDVM hVolMgr, bool fReadOnly,
+ PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
+{
+
+ pObjInfo->cbObject = RTDvmVolumeGetSize(hVolume);
+ pObjInfo->cbAllocated = pObjInfo->cbObject;
+ RTTimeSpecSetNano(&pObjInfo->AccessTime, 0);
+ RTTimeSpecSetNano(&pObjInfo->ModificationTime, 0);
+ RTTimeSpecSetNano(&pObjInfo->ChangeTime, 0);
+ RTTimeSpecSetNano(&pObjInfo->BirthTime, 0);
+ pObjInfo->Attr.fMode = RTFS_TYPE_FILE | RTFS_DOS_NT_NORMAL;
+ if (fReadOnly)
+ pObjInfo->Attr.fMode |= RTFS_DOS_READONLY | 0444;
+ else
+ pObjInfo->Attr.fMode |= 0666;
+
+ return rtDvmVfsFileSym_QueryAddAttrWorker(hVolume, hVolMgr, pObjInfo, enmAddAttr);
+}
+
+
+/**
+ * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
+ */
+static DECLCALLBACK(int) rtDvmVfsFile_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
+{
+ PRTVFSDVMFILE pThis = (PRTVFSDVMFILE)pvThis;
+ return rtDvmVfsFile_QueryInfoWorker(pThis->hVol,
+ pThis->pVfsVol ? pThis->pVfsVol->hVolMgr : NIL_RTDVM,
+ pThis->pVfsVol ? pThis->pVfsVol->fReadOnly : !pThis->fCanWrite,
+ pObjInfo, enmAddAttr);
+}
+
+
+/**
+ * @interface_method_impl{RTVFSIOSTREAMOPS,pfnRead}
+ */
+static DECLCALLBACK(int) rtDvmVfsFile_Read(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbRead)
+{
+ PRTVFSDVMFILE pThis = (PRTVFSDVMFILE)pvThis;
+ int rc = VINF_SUCCESS;
+
+ 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;
+ if (offUnsigned >= RTDvmVolumeGetSize(pThis->hVol))
+ {
+ if (pcbRead)
+ {
+ *pcbRead = 0;
+ pThis->offCurPos = offUnsigned;
+ return VINF_EOF;
+ }
+ return VERR_EOF;
+ }
+
+ size_t cbLeftToRead;
+ if (offUnsigned + pSgBuf->paSegs[0].cbSeg > RTDvmVolumeGetSize(pThis->hVol))
+ {
+ if (!pcbRead)
+ return VERR_EOF;
+ *pcbRead = cbLeftToRead = (size_t)(RTDvmVolumeGetSize(pThis->hVol) - offUnsigned);
+ }
+ else
+ {
+ cbLeftToRead = pSgBuf->paSegs[0].cbSeg;
+ if (pcbRead)
+ *pcbRead = cbLeftToRead;
+ }
+
+ /*
+ * Ok, we've got a valid stretch within the file. Do the reading.
+ */
+ if (cbLeftToRead > 0)
+ {
+ rc = RTDvmVolumeRead(pThis->hVol, offUnsigned, pSgBuf->paSegs[0].pvSeg, cbLeftToRead);
+ if (RT_SUCCESS(rc))
+ offUnsigned += cbLeftToRead;
+ }
+
+ pThis->offCurPos = offUnsigned;
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSIOSTREAMOPS,pfnWrite}
+ */
+static DECLCALLBACK(int) rtDvmVfsFile_Write(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbWritten)
+{
+ PRTVFSDVMFILE pThis = (PRTVFSDVMFILE)pvThis;
+ int rc = VINF_SUCCESS;
+
+ 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;
+ if (offUnsigned >= RTDvmVolumeGetSize(pThis->hVol))
+ {
+ if (pcbWritten)
+ {
+ *pcbWritten = 0;
+ pThis->offCurPos = offUnsigned;
+ }
+ return VERR_NOT_SUPPORTED;
+ }
+
+ size_t cbLeftToWrite;
+ if (offUnsigned + pSgBuf->paSegs[0].cbSeg > RTDvmVolumeGetSize(pThis->hVol))
+ {
+ if (!pcbWritten)
+ return VERR_EOF;
+ *pcbWritten = cbLeftToWrite = (size_t)(RTDvmVolumeGetSize(pThis->hVol) - offUnsigned);
+ }
+ else
+ {
+ cbLeftToWrite = pSgBuf->paSegs[0].cbSeg;
+ if (pcbWritten)
+ *pcbWritten = cbLeftToWrite;
+ }
+
+ /*
+ * Ok, we've got a valid stretch within the file. Do the reading.
+ */
+ if (cbLeftToWrite > 0)
+ {
+ rc = RTDvmVolumeWrite(pThis->hVol, 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) rtDvmVfsFile_Flush(void *pvThis)
+{
+ NOREF(pvThis);
+ return VINF_SUCCESS; /** @todo Implement missing DVM API. */
+}
+
+
+/**
+ * @interface_method_impl{RTVFSIOSTREAMOPS,pfnTell}
+ */
+static DECLCALLBACK(int) rtDvmVfsFile_Tell(void *pvThis, PRTFOFF poffActual)
+{
+ PRTVFSDVMFILE pThis = (PRTVFSDVMFILE)pvThis;
+ *poffActual = pThis->offCurPos;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSOBJSETOPS,pfnMode}
+ */
+static DECLCALLBACK(int) rtDvmVfsFile_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) rtDvmVfsFile_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) rtDvmVfsFile_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) rtDvmVfsFile_Seek(void *pvThis, RTFOFF offSeek, unsigned uMethod, PRTFOFF poffActual)
+{
+ PRTVFSDVMFILE pThis = (PRTVFSDVMFILE)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 = RTDvmVolumeGetSize(pThis->hVol);
+ break;
+
+ default:
+ return VERR_INTERNAL_ERROR_5;
+ }
+
+ /*
+ * Calc new position, take care to stay within bounds.
+ *
+ * @todo: Setting position beyond the end of the volume does not make sense.
+ */
+ 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) rtDvmVfsFile_QuerySize(void *pvThis, uint64_t *pcbFile)
+{
+ PRTVFSDVMFILE pThis = (PRTVFSDVMFILE)pvThis;
+ *pcbFile = RTDvmVolumeGetSize(pThis->hVol);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Standard file operations.
+ */
+DECL_HIDDEN_CONST(const RTVFSFILEOPS) g_rtDvmVfsStdFileOps =
+{
+ { /* Stream */
+ { /* Obj */
+ RTVFSOBJOPS_VERSION,
+ RTVFSOBJTYPE_FILE,
+ "DvmFile",
+ rtDvmVfsFile_Close,
+ rtDvmVfsFile_QueryInfo,
+ NULL,
+ RTVFSOBJOPS_VERSION
+ },
+ RTVFSIOSTREAMOPS_VERSION,
+ RTVFSIOSTREAMOPS_FEAT_NO_SG,
+ rtDvmVfsFile_Read,
+ rtDvmVfsFile_Write,
+ rtDvmVfsFile_Flush,
+ NULL /*pfnPollOne*/,
+ rtDvmVfsFile_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),
+ rtDvmVfsFile_SetMode,
+ rtDvmVfsFile_SetTimes,
+ rtDvmVfsFile_SetOwner,
+ RTVFSOBJSETOPS_VERSION
+ },
+ rtDvmVfsFile_Seek,
+ rtDvmVfsFile_QuerySize,
+ NULL /*SetSize*/,
+ NULL /*QueryMaxSize*/,
+ RTVFSFILEOPS_VERSION
+};
+
+
+/**
+ * Internal worker for RTDvmVolumeCreateVfsFile and rtDvmVfsDir_OpenFile.
+ *
+ * @returns IPRT status code.
+ * @param pVfsVol The VFS volume, optional.
+ * @param hVol The volume handle. (Reference not consumed.)
+ * @param fOpen RTFILE_O_XXX (valid).
+ * @param phVfsFileOut Where to return the handle to the file.
+ */
+static int rtDvmVfsCreateFileForVolume(PRTDVMVFSVOL pVfsVol, RTDVMVOLUME hVol, uint64_t fOpen, PRTVFSFILE phVfsFileOut)
+{
+ uint32_t cRefs = RTDvmVolumeRetain(hVol);
+ AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
+
+ /*
+ * Create the volume file.
+ */
+ RTVFSFILE hVfsFile;
+ PRTVFSDVMFILE pThis;
+ int rc = RTVfsNewFile(&g_rtDvmVfsStdFileOps, sizeof(*pThis), fOpen, NIL_RTVFS, NIL_RTVFSLOCK, &hVfsFile, (void **)&pThis);
+ if (RT_SUCCESS(rc))
+ {
+ pThis->offCurPos = 0;
+ pThis->hVol = hVol;
+ pThis->fCanRead = RT_BOOL(fOpen & RTFILE_O_READ);
+ pThis->fCanWrite = RT_BOOL(fOpen & RTFILE_O_WRITE);
+ pThis->pVfsVol = pVfsVol;
+
+ *phVfsFileOut = hVfsFile;
+ return VINF_SUCCESS;
+ }
+
+ RTDvmVolumeRelease(hVol);
+ return rc;
+}
+
+
+RTDECL(int) RTDvmVolumeCreateVfsFile(RTDVMVOLUME hVol, uint64_t fOpen, PRTVFSFILE phVfsFileOut)
+{
+ AssertPtrReturn(hVol, VERR_INVALID_HANDLE);
+ AssertPtrReturn(phVfsFileOut, VERR_INVALID_POINTER);
+ AssertReturn(fOpen & RTFILE_O_ACCESS_MASK, VERR_INVALID_FLAGS);
+ AssertReturn(!(fOpen & ~RTFILE_O_VALID_MASK), VERR_INVALID_FLAGS);
+ return rtDvmVfsCreateFileForVolume(NULL, hVol, fOpen, phVfsFileOut);
+}
+
+
+/*********************************************************************************************************************************
+* DVM Symbolic Link Objects *
+*********************************************************************************************************************************/
+/**
+ * @interface_method_impl{RTVFSOBJOPS,pfnClose}
+ */
+static DECLCALLBACK(int) rtDvmVfsSym_Close(void *pvThis)
+{
+ PRTVFSDVMSYMLINK pThis = (PRTVFSDVMSYMLINK)pvThis;
+ if (pThis->pszSymlink)
+ {
+ RTStrFree(pThis->pszSymlink);
+ pThis->pszSymlink = NULL;
+ }
+ if (pThis->hVol != NIL_RTDVMVOLUME)
+ {
+ RTDvmVolumeRelease(pThis->hVol);
+ pThis->hVol = NIL_RTDVMVOLUME;
+ }
+ if (pThis->hVolMgr != NIL_RTDVM)
+ {
+ RTDvmRelease(pThis->hVolMgr);
+ pThis->hVolMgr = NIL_RTDVM;
+ }
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Worker for rtDvmVfsSym_QueryInfo and rtDvmVfsDir_Read.
+ *
+ * @returns IPRT status code.
+ * @param hVolume The volume handle.
+ * @param hVolMgr The volume manager handle. Optional.
+ * @param pszTarget The link target.
+ * @param pObjInfo The object info structure to populate.
+ * @param enmAddAttr The additional attributes to supply.
+ */
+static int rtDvmVfsSym_QueryInfoWorker(RTDVMVOLUME hVolume, RTDVM hVolMgr, const char *pszTarget,
+ PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
+{
+ RT_ZERO(*pObjInfo);
+ pObjInfo->cbObject = pObjInfo->cbAllocated = pszTarget ? strlen(pszTarget) : 0;
+ pObjInfo->Attr.fMode = 0777 | RTFS_TYPE_SYMLINK | RTFS_DOS_NT_REPARSE_POINT;
+
+ return rtDvmVfsFileSym_QueryAddAttrWorker(hVolume, hVolMgr, pObjInfo, enmAddAttr);
+}
+
+
+/**
+ * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
+ */
+static DECLCALLBACK(int) rtDvmVfsSym_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
+{
+ PRTVFSDVMSYMLINK pThis = (PRTVFSDVMSYMLINK)pvThis;
+ return rtDvmVfsSym_QueryInfoWorker(pThis->hVol, pThis->hVolMgr, pThis->szTarget, pObjInfo, enmAddAttr);
+}
+
+
+/**
+ * @interface_method_impl{RTVFSSYMLINKOPS,pfnRead}
+ */
+static DECLCALLBACK(int) rtDvmVfsSym_Read(void *pvThis, char *pszTarget, size_t cbTarget)
+{
+ PRTVFSDVMSYMLINK pThis = (PRTVFSDVMSYMLINK)pvThis;
+ return RTStrCopy(pszTarget, cbTarget, pThis->szTarget);
+}
+
+
+/**
+ * DVM symbolic link operations.
+ */
+static const RTVFSSYMLINKOPS g_rtDvmVfsSymOps =
+{
+ { /* Obj */
+ RTVFSOBJOPS_VERSION,
+ RTVFSOBJTYPE_SYMLINK,
+ "DvmSymlink",
+ rtDvmVfsSym_Close,
+ rtDvmVfsSym_QueryInfo,
+ NULL,
+ RTVFSOBJOPS_VERSION
+ },
+ RTVFSSYMLINKOPS_VERSION,
+ 0,
+ { /* ObjSet */
+ RTVFSOBJSETOPS_VERSION,
+ RT_UOFFSETOF(RTVFSSYMLINKOPS, ObjSet) - RT_UOFFSETOF(RTVFSSYMLINKOPS, Obj),
+ NULL /*rtDvmVfsSym_SetMode*/,
+ NULL /*rtDvmVfsSym_SetTimes*/,
+ NULL /*rtDvmVfsSym_SetOwner*/,
+ RTVFSOBJSETOPS_VERSION
+ },
+ rtDvmVfsSym_Read,
+ RTVFSSYMLINKOPS_VERSION
+};
+
+
+/**
+ * Internal worker for rtDvmVfsDir_OpenFile.
+ *
+ * @returns IPRT status code.
+ * @param hVol The volume handle (not consumed).
+ * @param hVolMgr The volume manager handle (not consumed).
+ * @param iVol The volume number.
+ * @param pszSymlink The volume name. Consumed on success.
+ * @param phVfsSymlinkOut Where to return the handle to the file.
+ */
+static int rtDvmVfsCreateSymlinkForVolume(RTDVMVOLUME hVol, RTDVM hVolMgr, uint32_t iVol, char *pszSymlink,
+ PRTVFSSYMLINK phVfsSymlinkOut)
+{
+ uint32_t cRefs = RTDvmVolumeRetain(hVol);
+ AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
+
+ cRefs = RTDvmRetain(hVolMgr);
+ AssertReturnStmt(cRefs != UINT32_MAX, RTDvmVolumeRelease(hVol), VERR_INVALID_HANDLE);
+
+ /*
+ * Create the symlink.
+ */
+ RTVFSSYMLINK hVfsSym;
+ PRTVFSDVMSYMLINK pThis;
+ int rc = RTVfsNewSymlink(&g_rtDvmVfsSymOps, sizeof(*pThis), NIL_RTVFS, NIL_RTVFSLOCK, &hVfsSym, (void **)&pThis);
+ if (RT_SUCCESS(rc))
+ {
+ pThis->hVol = hVol;
+ pThis->hVolMgr = hVolMgr;
+ pThis->pszSymlink = pszSymlink;
+ RTStrPrintf(pThis->szTarget, sizeof(pThis->szTarget), "vol%u", iVol);
+
+ *phVfsSymlinkOut = hVfsSym;
+ return VINF_SUCCESS;
+ }
+ RTDvmRelease(hVolMgr);
+ RTDvmVolumeRelease(hVol);
+ return rc;
+}
+
+
+
+/*********************************************************************************************************************************
+* DVM Directory Objects *
+*********************************************************************************************************************************/
+
+/**
+ * @interface_method_impl{RTVFSOBJOPS,pfnClose}
+ */
+static DECLCALLBACK(int) rtDvmVfsDir_Close(void *pvThis)
+{
+ PRTDVMVFSDIR pThis = (PRTDVMVFSDIR)pvThis;
+
+ if (pThis->hCurVolume != NIL_RTDVMVOLUME)
+ {
+ RTDvmVolumeRelease(pThis->hCurVolume);
+ pThis->hCurVolume = NIL_RTDVMVOLUME;
+ }
+
+ if (pThis->pszNameAlias)
+ {
+ RTStrFree(pThis->pszNameAlias);
+ pThis->pszNameAlias = NULL;
+ }
+
+ pThis->pVfsVol = NULL;
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
+ */
+static DECLCALLBACK(int) rtDvmVfsDir_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
+{
+ PRTDVMVFSDIR pThis = (PRTDVMVFSDIR)pvThis;
+ pObjInfo->cbObject = pThis->pVfsVol->cVolumes;
+ pObjInfo->cbAllocated = pThis->pVfsVol->cVolumes;
+ RTTimeSpecSetNano(&pObjInfo->AccessTime, 0);
+ RTTimeSpecSetNano(&pObjInfo->ModificationTime, 0);
+ RTTimeSpecSetNano(&pObjInfo->ChangeTime, 0);
+ RTTimeSpecSetNano(&pObjInfo->BirthTime, 0);
+ pObjInfo->Attr.fMode = RTFS_TYPE_DIRECTORY | RTFS_DOS_DIRECTORY;
+ if (pThis->pVfsVol->fReadOnly)
+ pObjInfo->Attr.fMode |= RTFS_DOS_READONLY | 0555;
+ else
+ pObjInfo->Attr.fMode |= 0777;
+
+ switch (enmAddAttr)
+ {
+ case RTFSOBJATTRADD_NOTHING:
+ case RTFSOBJATTRADD_UNIX:
+ pObjInfo->Attr.u.Unix.uid = NIL_RTUID;
+ pObjInfo->Attr.u.Unix.gid = (RTGID)RTDvmMapGetFormatType(pThis->pVfsVol->hVolMgr);
+ pObjInfo->Attr.u.Unix.cHardlinks = pThis->pVfsVol->cVolumes;
+ 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 = (RTGID)RTDvmMapGetFormatType(pThis->pVfsVol->hVolMgr);
+ RTStrCopy(pObjInfo->Attr.u.UnixGroup.szName, sizeof(pObjInfo->Attr.u.UnixGroup.szName),
+ RTDvmMapGetFormatName(pThis->pVfsVol->hVolMgr));
+ break;
+
+ case RTFSOBJATTRADD_EASIZE:
+ pObjInfo->Attr.u.EASize.cb = 0;
+ break;
+
+ default:
+ return VERR_INVALID_PARAMETER;
+ }
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSOBJSETOPS,pfnMode}
+ */
+static DECLCALLBACK(int) rtDvmVfsDir_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) rtDvmVfsDir_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) rtDvmVfsDir_SetOwner(void *pvThis, RTUID uid, RTGID gid)
+{
+ RT_NOREF(pvThis, uid, gid);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+static int rtDvmVfsDir_FindEntry(PRTDVMVFSDIR pThis, const char *pszEntry,
+ PRTDVMVOLUME phVolume, uint32_t *piVol, char **ppszSymlink)
+{
+ *phVolume = NIL_RTDVMVOLUME;
+ *ppszSymlink = NULL;
+ *piVol = UINT32_MAX;
+
+ /*
+ * Enumerate the volumes and try match the volume name.
+ */
+ int rc;
+ PRTDVMVFSVOL pVfsVol = pThis->pVfsVol;
+ if (pVfsVol->cVolumes > 0)
+ {
+ /* The first volume. */
+ uint32_t iVol = 0;
+ RTDVMVOLUME hVol;
+ rc = RTDvmMapQueryFirstVolume(pThis->pVfsVol->hVolMgr, &hVol);
+ while (RT_SUCCESS(rc))
+ {
+ /* Match the name. */
+ bool fMatch;
+ char *pszVolName;
+ rc = RTDvmVolumeQueryName(hVol, &pszVolName);
+ if (RT_SUCCESS(rc))
+ {
+ fMatch = RTStrCmp(pszEntry, pszVolName) == 0 && *pszVolName != '\0';
+ if (fMatch)
+ {
+ *phVolume = hVol;
+ *ppszSymlink = pszVolName;
+ *piVol = iVol;
+ return VINF_SUCCESS;
+ }
+ RTStrFree(pszVolName);
+ }
+ else if (rc == VERR_NOT_SUPPORTED)
+ fMatch = false;
+ else
+ {
+ RTDvmVolumeRelease(hVol);
+ break;
+ }
+
+ /* Match the sequential volume number. */
+ if (!fMatch)
+ {
+ char szTmp[16];
+ RTStrPrintf(szTmp, sizeof(szTmp), "vol%u", iVol);
+ fMatch = RTStrCmp(pszEntry, szTmp) == 0;
+ }
+
+ if (fMatch)
+ {
+ *phVolume = hVol;
+ *piVol = iVol;
+ return VINF_SUCCESS;
+ }
+
+ /* More volumes? */
+ iVol++;
+ if (iVol >= pVfsVol->cVolumes)
+ {
+ RTDvmVolumeRelease(hVol);
+ rc = VERR_FILE_NOT_FOUND;
+ break;
+ }
+
+ /* Get the next volume. */
+ RTDVMVOLUME hVolNext;
+ rc = RTDvmMapQueryNextVolume(pThis->pVfsVol->hVolMgr, hVol, &hVolNext);
+ RTDvmVolumeRelease(hVol);
+ hVol = hVolNext;
+ }
+ }
+ else
+ rc = VERR_FILE_NOT_FOUND;
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSDIROPS,pfnOpen}
+ */
+static DECLCALLBACK(int) rtDvmVfsDir_Open(void *pvThis, const char *pszEntry, uint64_t fOpen, uint32_t fFlags, PRTVFSOBJ phVfsObj)
+{
+ PRTDVMVFSDIR pThis = (PRTDVMVFSDIR)pvThis;
+
+ /*
+ * Special case: '.' and '..'
+ */
+ if ( pszEntry[0] == '.'
+ && ( pszEntry[1] == '\0'
+ || ( pszEntry[1] == '.'
+ && pszEntry[2] == '\0')))
+ {
+ if ( (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN
+ || (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN_CREATE
+ || (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_CREATE_REPLACE)
+ {
+ if (fFlags & RTVFSOBJ_F_OPEN_DIRECTORY)
+ {
+ RTVFSDIR hVfsDir;
+ int rc = rtDvmVfsVol_OpenRoot(pThis->pVfsVol, &hVfsDir);
+ if (RT_SUCCESS(rc))
+ {
+ *phVfsObj = RTVfsObjFromDir(hVfsDir);
+ RTVfsDirRelease(hVfsDir);
+ AssertStmt(*phVfsObj != NIL_RTVFSOBJ, rc = VERR_INTERNAL_ERROR_3);
+ }
+ return rc;
+ }
+ return VERR_IS_A_DIRECTORY;
+ }
+ return VERR_ACCESS_DENIED;
+ }
+
+ /*
+ * Open volume file.
+ */
+ RTDVMVOLUME hVolume = NIL_RTDVMVOLUME;
+ uint32_t iVol = 0;
+ char *pszSymlink = NULL;
+ int rc = rtDvmVfsDir_FindEntry(pThis, pszEntry, &hVolume, &iVol, &pszSymlink);
+ if (RT_SUCCESS(rc))
+ {
+ if ( (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN
+ || (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN_CREATE
+ || (fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_CREATE_REPLACE)
+ {
+ if (fFlags & (RTVFSOBJ_F_OPEN_FILE | RTVFSOBJ_F_OPEN_DEV_BLOCK))
+ {
+ if (!pszSymlink)
+ {
+ if ( !(fOpen & RTFILE_O_WRITE)
+ || !pThis->pVfsVol->fReadOnly)
+ {
+ /* Create file object. */
+ RTVFSFILE hVfsFile;
+ rc = rtDvmVfsCreateFileForVolume(pThis->pVfsVol, hVolume, fOpen, &hVfsFile);
+ if (RT_SUCCESS(rc))
+ {
+ *phVfsObj = RTVfsObjFromFile(hVfsFile);
+ RTVfsFileRelease(hVfsFile);
+ AssertStmt(*phVfsObj != NIL_RTVFSOBJ, rc = VERR_INTERNAL_ERROR_3);
+ }
+ }
+ else
+ rc = VERR_WRITE_PROTECT;
+ }
+ else
+ rc = VERR_IS_A_SYMLINK;
+ }
+ else if (fFlags & RTVFSOBJ_F_OPEN_SYMLINK)
+ {
+ /* Create symlink object */
+ RTVFSSYMLINK hVfsSym = NIL_RTVFSSYMLINK; /* (older gcc maybe used uninitialized) */
+ rc = rtDvmVfsCreateSymlinkForVolume(hVolume, pThis->pVfsVol ? pThis->pVfsVol->hVolMgr : NIL_RTDVM, iVol,
+ pszSymlink, &hVfsSym);
+ if (RT_SUCCESS(rc))
+ {
+ *phVfsObj = RTVfsObjFromSymlink(hVfsSym);
+ RTVfsSymlinkRelease(hVfsSym);
+ AssertStmt(*phVfsObj != NIL_RTVFSOBJ, rc = VERR_INTERNAL_ERROR_3);
+ pszSymlink = NULL;
+ }
+ }
+ else
+ rc = VERR_IS_A_FILE;
+ }
+ else
+ rc = VERR_ALREADY_EXISTS;
+ RTDvmVolumeRelease(hVolume);
+ if (pszSymlink)
+ RTStrFree(pszSymlink);
+ }
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSDIROPS,pfnOpenFile}
+ */
+static DECLCALLBACK(int) rtDvmVfsDir_OpenFile(void *pvThis, const char *pszFilename, uint64_t fOpen, PRTVFSFILE phVfsFile)
+{
+ RTVFSOBJ hVfsObj;
+ int rc = rtDvmVfsDir_Open(pvThis, pszFilename, fOpen, RTVFSOBJ_F_OPEN_FILE, &hVfsObj);
+ if (RT_SUCCESS(rc))
+ {
+ *phVfsFile = RTVfsObjToFile(hVfsObj);
+ RTVfsObjRelease(hVfsObj);
+ }
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSDIROPS,pfnCreateDir}
+ */
+static DECLCALLBACK(int) rtDvmVfsDir_CreateDir(void *pvThis, const char *pszSubDir, RTFMODE fMode, PRTVFSDIR phVfsDir)
+{
+ RT_NOREF(pvThis, pszSubDir, fMode, phVfsDir);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSDIROPS,pfnOpenSymlink}
+ */
+static DECLCALLBACK(int) rtDvmVfsDir_OpenSymlink(void *pvThis, const char *pszSymlink, PRTVFSSYMLINK phVfsSymlink)
+{
+ RT_NOREF(pvThis, pszSymlink, phVfsSymlink);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSDIROPS,pfnCreateSymlink}
+ */
+static DECLCALLBACK(int) rtDvmVfsDir_CreateSymlink(void *pvThis, const char *pszSymlink, const char *pszTarget,
+ RTSYMLINKTYPE enmType, PRTVFSSYMLINK phVfsSymlink)
+{
+ RT_NOREF(pvThis, pszSymlink, pszTarget, enmType, phVfsSymlink);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSDIROPS,pfnUnlinkEntry}
+ */
+static DECLCALLBACK(int) rtDvmVfsDir_UnlinkEntry(void *pvThis, const char *pszEntry, RTFMODE fType)
+{
+ RT_NOREF(pvThis, pszEntry, fType);
+ return VERR_NOT_IMPLEMENTED;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSDIROPS,pfnRenameEntry}
+ */
+static DECLCALLBACK(int) rtDvmVfsDir_RenameEntry(void *pvThis, const char *pszEntry, RTFMODE fType, const char *pszNewName)
+{
+ RT_NOREF(pvThis, pszEntry, fType, pszNewName);
+ return VERR_NOT_IMPLEMENTED;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSDIROPS,pfnRewindDir}
+ */
+static DECLCALLBACK(int) rtDvmVfsDir_RewindDir(void *pvThis)
+{
+ PRTDVMVFSDIR pThis = (PRTDVMVFSDIR)pvThis;
+
+ if (pThis->hCurVolume != NIL_RTDVMVOLUME)
+ {
+ RTDvmVolumeRelease(pThis->hCurVolume);
+ pThis->hCurVolume = NIL_RTDVMVOLUME;
+ }
+ pThis->fReturnCurrent = false;
+ pThis->offDir = 0;
+ if (pThis->pszNameAlias)
+ {
+ RTStrFree(pThis->pszNameAlias);
+ pThis->pszNameAlias = NULL;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSDIROPS,pfnReadDir}
+ */
+static DECLCALLBACK(int) rtDvmVfsDir_ReadDir(void *pvThis, PRTDIRENTRYEX pDirEntry, size_t *pcbDirEntry,
+ RTFSOBJATTRADD enmAddAttr)
+{
+ PRTDVMVFSDIR pThis = (PRTDVMVFSDIR)pvThis;
+ PRTDVMVFSVOL pVfsVol = pThis->pVfsVol;
+ int rc;
+
+ /*
+ * Format the volume name since we'll be needing it all but the final call.
+ */
+ char szVolNo[16];
+ size_t const cchVolNo = RTStrPrintf(szVolNo, sizeof(szVolNo), "vol%u", pThis->offDir);
+
+ if (!pThis->fReturnCurrent)
+ {
+ /*
+ * Do we have a pending name alias to return?
+ */
+ if (pThis->pszNameAlias)
+ {
+ size_t cchNameAlias = strlen(pThis->pszNameAlias);
+ size_t cbNeeded = RT_UOFFSETOF_DYN(RTDIRENTRYEX, szName[cchNameAlias + 1]);
+ if (cbNeeded <= *pcbDirEntry)
+ {
+ *pcbDirEntry = cbNeeded;
+
+ /* Do the names. */
+ pDirEntry->cbName = (uint16_t)cchNameAlias;
+ memcpy(pDirEntry->szName, pThis->pszNameAlias, cchNameAlias + 1);
+ pDirEntry->cwcShortName = 0;
+ pDirEntry->wszShortName[0] = '\0';
+
+
+ /* Do the rest. */
+ rc = rtDvmVfsSym_QueryInfoWorker(pThis->hCurVolume, pVfsVol->hVolMgr, szVolNo, &pDirEntry->Info, enmAddAttr);
+ if (RT_SUCCESS(rc))
+ {
+ RTStrFree(pThis->pszNameAlias);
+ pThis->pszNameAlias = NULL;
+ pThis->offDir += 1;
+ }
+ return rc;
+ }
+
+ *pcbDirEntry = cbNeeded;
+ return VERR_BUFFER_OVERFLOW;
+ }
+
+ /*
+ * Get the next volume to return info about.
+ */
+ if (pThis->offDir < pVfsVol->cVolumes)
+ {
+ RTDVMVOLUME hNextVolume;
+ if (pThis->offDir == 0)
+ rc = RTDvmMapQueryFirstVolume(pVfsVol->hVolMgr, &hNextVolume);
+ else
+ rc = RTDvmMapQueryNextVolume(pVfsVol->hVolMgr, pThis->hCurVolume, &hNextVolume);
+ if (RT_FAILURE(rc))
+ return rc;
+ RTDvmVolumeRelease(pThis->hCurVolume);
+ pThis->hCurVolume = hNextVolume;
+
+ /* Check if we need to return a name alias later. */
+ rc = RTDvmVolumeQueryName(pThis->hCurVolume, &pThis->pszNameAlias);
+ if (RT_FAILURE(rc))
+ pThis->pszNameAlias = NULL;
+ else if (*pThis->pszNameAlias == '\0')
+ {
+ RTStrFree(pThis->pszNameAlias);
+ pThis->pszNameAlias = NULL;
+ }
+ }
+ else
+ {
+ RTDvmVolumeRelease(pThis->hCurVolume);
+ pThis->hCurVolume = NIL_RTDVMVOLUME;
+ return VERR_NO_MORE_FILES;
+ }
+ }
+
+ /*
+ * Figure out the name length.
+ */
+ size_t cbNeeded = RT_UOFFSETOF_DYN(RTDIRENTRYEX, szName[cchVolNo + 1]);
+ if (cbNeeded <= *pcbDirEntry)
+ {
+ *pcbDirEntry = cbNeeded;
+
+ /* Do the names. */
+ pDirEntry->cbName = (uint16_t)cchVolNo;
+ memcpy(pDirEntry->szName, szVolNo, cchVolNo + 1);
+ pDirEntry->cwcShortName = 0;
+ pDirEntry->wszShortName[0] = '\0';
+
+ /* Do the rest. */
+ rc = rtDvmVfsFile_QueryInfoWorker(pThis->hCurVolume, pVfsVol->hVolMgr, pVfsVol->fReadOnly, &pDirEntry->Info, enmAddAttr);
+ if (RT_SUCCESS(rc))
+ {
+ pThis->fReturnCurrent = false;
+ if (!pThis->pszNameAlias)
+ pThis->offDir += 1;
+ return rc;
+ }
+ }
+ else
+ {
+ *pcbDirEntry = cbNeeded;
+ rc = VERR_BUFFER_OVERFLOW;
+ }
+ pThis->fReturnCurrent = true;
+ return rc;
+}
+
+
+/**
+ * DVM (root) directory operations.
+ */
+static const RTVFSDIROPS g_rtDvmVfsDirOps =
+{
+ { /* Obj */
+ RTVFSOBJOPS_VERSION,
+ RTVFSOBJTYPE_DIR,
+ "DvmDir",
+ rtDvmVfsDir_Close,
+ rtDvmVfsDir_QueryInfo,
+ NULL,
+ RTVFSOBJOPS_VERSION
+ },
+ RTVFSDIROPS_VERSION,
+ 0,
+ { /* ObjSet */
+ RTVFSOBJSETOPS_VERSION,
+ RT_UOFFSETOF(RTVFSDIROPS, ObjSet) - RT_UOFFSETOF(RTVFSDIROPS, Obj),
+ rtDvmVfsDir_SetMode,
+ rtDvmVfsDir_SetTimes,
+ rtDvmVfsDir_SetOwner,
+ RTVFSOBJSETOPS_VERSION
+ },
+ rtDvmVfsDir_Open,
+ NULL /* pfnFollowAbsoluteSymlink */,
+ rtDvmVfsDir_OpenFile,
+ NULL /* pfnOpenDir */,
+ rtDvmVfsDir_CreateDir,
+ rtDvmVfsDir_OpenSymlink,
+ rtDvmVfsDir_CreateSymlink,
+ NULL /* pfnQueryEntryInfo */,
+ rtDvmVfsDir_UnlinkEntry,
+ rtDvmVfsDir_RenameEntry,
+ rtDvmVfsDir_RewindDir,
+ rtDvmVfsDir_ReadDir,
+ RTVFSDIROPS_VERSION,
+};
+
+
+
+/**
+ * @interface_method_impl{RTVFSOBJOPS::Obj,pfnClose}
+ */
+static DECLCALLBACK(int) rtDvmVfsVol_Close(void *pvThis)
+{
+ PRTDVMVFSVOL pThis = (PRTDVMVFSVOL)pvThis;
+ LogFlow(("rtDvmVfsVol_Close(%p)\n", pThis));
+
+ if ( pThis->fCloseDvm
+ && pThis->hVolMgr != NIL_RTDVM )
+ RTDvmRelease(pThis->hVolMgr);
+ pThis->hVolMgr = NIL_RTDVM;
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSOBJOPS::Obj,pfnQueryInfo}
+ */
+static DECLCALLBACK(int) rtDvmVfsVol_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
+{
+ RT_NOREF(pvThis, pObjInfo, enmAddAttr);
+ return VERR_WRONG_TYPE;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSOPS,pfnOpenRoot}
+ */
+static DECLCALLBACK(int) rtDvmVfsVol_OpenRoot(void *pvThis, PRTVFSDIR phVfsDir)
+{
+ PRTDVMVFSVOL pThis = (PRTDVMVFSVOL)pvThis;
+
+ PRTDVMVFSDIR pNewDir;
+ int rc = RTVfsNewDir(&g_rtDvmVfsDirOps, sizeof(*pNewDir), 0 /*fFlags*/, pThis->hVfsSelf,
+ NIL_RTVFSLOCK /*use volume lock*/, phVfsDir, (void **)&pNewDir);
+ if (RT_SUCCESS(rc))
+ {
+ pNewDir->offDir = 0;
+ pNewDir->pVfsVol = pThis;
+ pNewDir->fReturnCurrent = false;
+ pNewDir->pszNameAlias = NULL;
+ pNewDir->hCurVolume = NIL_RTDVMVOLUME;
+ }
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSOPS,pfnQueryRangeState}
+ */
+static DECLCALLBACK(int) rtDvmVfsVol_QueryRangeState(void *pvThis, uint64_t off, size_t cb, bool *pfUsed)
+{
+ RT_NOREF(pvThis, off, cb, pfUsed);
+ return VERR_NOT_IMPLEMENTED;
+}
+
+
+DECL_HIDDEN_CONST(const RTVFSOPS) g_rtDvmVfsVolOps =
+{
+ { /* Obj */
+ RTVFSOBJOPS_VERSION,
+ RTVFSOBJTYPE_VFS,
+ "DvmVol",
+ rtDvmVfsVol_Close,
+ rtDvmVfsVol_QueryInfo,
+ NULL,
+ RTVFSOBJOPS_VERSION
+ },
+ RTVFSOPS_VERSION,
+ 0 /* fFeatures */,
+ rtDvmVfsVol_OpenRoot,
+ rtDvmVfsVol_QueryRangeState,
+ RTVFSOPS_VERSION
+};
+
+
+
+/**
+ * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnValidate}
+ */
+static DECLCALLBACK(int) rtDvmVfsChain_Validate(PCRTVFSCHAINELEMENTREG pProviderReg, PRTVFSCHAINSPEC pSpec,
+ PRTVFSCHAINELEMSPEC pElement, uint32_t *poffError, PRTERRINFO pErrInfo)
+{
+ RT_NOREF(pProviderReg, pSpec);
+
+ /*
+ * Basic checks.
+ */
+ if (pElement->enmTypeIn != RTVFSOBJTYPE_FILE)
+ return pElement->enmTypeIn == RTVFSOBJTYPE_INVALID ? VERR_VFS_CHAIN_CANNOT_BE_FIRST_ELEMENT : VERR_VFS_CHAIN_TAKES_FILE;
+ if (pElement->enmType != RTVFSOBJTYPE_VFS)
+ return VERR_VFS_CHAIN_ONLY_VFS;
+
+ if (pElement->cArgs > 1)
+ return VERR_VFS_CHAIN_AT_MOST_ONE_ARG;
+
+ /*
+ * Parse the flag if present, save in pElement->uProvider.
+ */
+ /** @todo allow specifying sector size */
+ bool fReadOnly = (pSpec->fOpenFile & RTFILE_O_ACCESS_MASK) == RTFILE_O_READ;
+ if (pElement->cArgs > 0)
+ {
+ const char *psz = pElement->paArgs[0].psz;
+ if (*psz)
+ {
+ if ( !strcmp(psz, "ro")
+ || !strcmp(psz, "r"))
+ fReadOnly = true;
+ else if (!strcmp(psz, "rw"))
+ fReadOnly = false;
+ else
+ {
+ *poffError = pElement->paArgs[0].offSpec;
+ return RTErrInfoSet(pErrInfo, VERR_VFS_CHAIN_INVALID_ARGUMENT, "Expected 'ro' or 'rw' as argument");
+ }
+ }
+ }
+
+ pElement->uProvider = fReadOnly;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnInstantiate}
+ */
+static DECLCALLBACK(int) rtDvmVfsChain_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);
+
+ /*
+ * Instantiate the volume manager and open the map stuff.
+ */
+ RTVFSFILE hPrevVfsFile = RTVfsObjToFile(hPrevVfsObj);
+ AssertReturn(hPrevVfsFile != NIL_RTVFSFILE, VERR_VFS_CHAIN_CAST_FAILED);
+
+ RTDVM hVolMgr;
+ int rc = RTDvmCreate(&hVolMgr, hPrevVfsFile, 512, 0 /*fFlags*/);
+ RTVfsFileRelease(hPrevVfsFile);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTDvmMapOpen(hVolMgr);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Create a VFS instance for the volume manager.
+ */
+ RTVFS hVfs = NIL_RTVFS;
+ PRTDVMVFSVOL pThis = NULL;
+ rc = RTVfsNew(&g_rtDvmVfsVolOps, sizeof(RTDVMVFSVOL), NIL_RTVFS, RTVFSLOCK_CREATE_RW, &hVfs, (void **)&pThis);
+ if (RT_SUCCESS(rc))
+ {
+ pThis->hVolMgr = hVolMgr;
+ pThis->fCloseDvm = true;
+ pThis->fReadOnly = pElement->uProvider == (uint64_t)true;
+ pThis->cVolumes = RTDvmMapGetValidVolumes(hVolMgr);
+ pThis->hVfsSelf = hVfs;
+
+ *phVfsObj = RTVfsObjFromVfs(hVfs);
+ RTVfsRelease(hVfs);
+ return *phVfsObj != NIL_RTVFSOBJ ? VINF_SUCCESS : VERR_VFS_CHAIN_CAST_FAILED;
+ }
+ }
+ else
+ rc = RTErrInfoSetF(pErrInfo, rc, "RTDvmMapOpen failed: %Rrc", rc);
+ RTDvmRelease(hVolMgr);
+ }
+ else
+ rc = RTErrInfoSetF(pErrInfo, rc, "RTDvmCreate failed: %Rrc", rc);
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnCanReuseElement}
+ */
+static DECLCALLBACK(bool) rtDvmVfsChain_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 = */ "dvm",
+ /* ListEntry = */ { NULL, NULL },
+ /* pszHelp = */ "Opens a container image using the VD API.\n"
+ "Optionally takes one parameter 'ro' (read only) or 'rw' (read write).\n",
+ /* pfnValidate = */ rtDvmVfsChain_Validate,
+ /* pfnInstantiate = */ rtDvmVfsChain_Instantiate,
+ /* pfnCanReuseElement = */ rtDvmVfsChain_CanReuseElement,
+ /* uEndMarker = */ RTVFSCHAINELEMENTREG_VERSION
+};
+
+RTVFSCHAIN_AUTO_REGISTER_ELEMENT_PROVIDER(&g_rtVfsChainIsoFsVolReg, rtVfsChainIsoFsVolReg);
+