summaryrefslogtreecommitdiffstats
path: root/src/VBox/Runtime/common/vfs/vfsstdfile.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Runtime/common/vfs/vfsstdfile.cpp')
-rw-r--r--src/VBox/Runtime/common/vfs/vfsstdfile.cpp640
1 files changed, 640 insertions, 0 deletions
diff --git a/src/VBox/Runtime/common/vfs/vfsstdfile.cpp b/src/VBox/Runtime/common/vfs/vfsstdfile.cpp
new file mode 100644
index 00000000..33d13d9d
--- /dev/null
+++ b/src/VBox/Runtime/common/vfs/vfsstdfile.cpp
@@ -0,0 +1,640 @@
+/* $Id: vfsstdfile.cpp $ */
+/** @file
+ * IPRT - Virtual File System, Standard File Implementation.
+ */
+
+/*
+ * Copyright (C) 2010-2019 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.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE 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.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/vfs.h>
+#include <iprt/vfslowlevel.h>
+
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/file.h>
+#include <iprt/poll.h>
+#include <iprt/string.h>
+#include <iprt/thread.h>
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Private data of a standard file.
+ */
+typedef struct RTVFSSTDFILE
+{
+ /** The file handle. */
+ RTFILE hFile;
+ /** Whether to leave the handle open when the VFS handle is closed. */
+ bool fLeaveOpen;
+} RTVFSSTDFILE;
+/** Pointer to the private data of a standard file. */
+typedef RTVFSSTDFILE *PRTVFSSTDFILE;
+
+
+/**
+ * @interface_method_impl{RTVFSOBJOPS,pfnClose}
+ */
+static DECLCALLBACK(int) rtVfsStdFile_Close(void *pvThis)
+{
+ PRTVFSSTDFILE pThis = (PRTVFSSTDFILE)pvThis;
+
+ int rc;
+ if (!pThis->fLeaveOpen)
+ rc = RTFileClose(pThis->hFile);
+ else
+ rc = VINF_SUCCESS;
+ pThis->hFile = NIL_RTFILE;
+
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
+ */
+static DECLCALLBACK(int) rtVfsStdFile_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
+{
+ PRTVFSSTDFILE pThis = (PRTVFSSTDFILE)pvThis;
+ return RTFileQueryInfo(pThis->hFile, pObjInfo, enmAddAttr);
+}
+
+
+/**
+ * RTFileRead and RTFileReadAt does not return VINF_EOF or VINF_TRY_AGAIN, this
+ * function tries to fix this as best as it can.
+ *
+ * This fixing can be subject to races if some other thread or process is
+ * modifying the file size between the read and our size query here.
+ *
+ * @returns VINF_SUCCESS, VINF_EOF or VINF_TRY_AGAIN.
+ * @param pThis The instance data.
+ * @param off The offset parameter.
+ * @param cbToRead The number of bytes attempted read .
+ * @param cbActuallyRead The number of bytes actually read.
+ */
+DECLINLINE(int) rtVfsStdFile_ReadFixRC(PRTVFSSTDFILE pThis, RTFOFF off, size_t cbToRead, size_t cbActuallyRead)
+{
+ /* If the read returned less bytes than requested, it means the end of the
+ file has been reached. */
+ if (cbToRead > cbActuallyRead)
+ return VINF_EOF;
+
+ /* The other case here is the very special zero byte read at the end of the
+ file, where we're supposed to indicate EOF. */
+ if (cbToRead > 0)
+ return VINF_SUCCESS;
+
+ uint64_t cbFile;
+ int rc = RTFileGetSize(pThis->hFile, &cbFile);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ uint64_t off2;
+ if (off >= 0)
+ off2 = off;
+ else
+ {
+ rc = RTFileSeek(pThis->hFile, 0, RTFILE_SEEK_CURRENT, &off2);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+ return off2 >= cbFile ? VINF_EOF : VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSIOSTREAMOPS,pfnRead}
+ */
+static DECLCALLBACK(int) rtVfsStdFile_Read(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbRead)
+{
+ PRTVFSSTDFILE pThis = (PRTVFSSTDFILE)pvThis;
+ int rc;
+
+ NOREF(fBlocking);
+ if (pSgBuf->cSegs == 1)
+ {
+ if (off < 0)
+ rc = RTFileRead( pThis->hFile, pSgBuf->paSegs[0].pvSeg, pSgBuf->paSegs[0].cbSeg, pcbRead);
+ else
+ rc = RTFileReadAt(pThis->hFile, off, pSgBuf->paSegs[0].pvSeg, pSgBuf->paSegs[0].cbSeg, pcbRead);
+ if (rc == VINF_SUCCESS && pcbRead)
+ rc = rtVfsStdFile_ReadFixRC(pThis, off, pSgBuf->paSegs[0].cbSeg, *pcbRead);
+ }
+ else
+ {
+ size_t cbSeg = 0;
+ size_t cbRead = 0;
+ size_t cbReadSeg = 0;
+ rc = VINF_SUCCESS;
+
+ for (uint32_t iSeg = 0; iSeg < pSgBuf->cSegs; iSeg++)
+ {
+ void *pvSeg = pSgBuf->paSegs[iSeg].pvSeg;
+ cbSeg = pSgBuf->paSegs[iSeg].cbSeg;
+
+ cbReadSeg = cbSeg;
+ if (off < 0)
+ rc = RTFileRead( pThis->hFile, pvSeg, cbSeg, pcbRead ? &cbReadSeg : NULL);
+ else
+ rc = RTFileReadAt(pThis->hFile, off, pvSeg, cbSeg, pcbRead ? &cbReadSeg : NULL);
+ if (RT_FAILURE(rc))
+ break;
+ if (off >= 0)
+ off += cbReadSeg;
+ cbRead += cbReadSeg;
+ if ((pcbRead && cbReadSeg != cbSeg) || rc != VINF_SUCCESS)
+ break;
+ }
+
+ if (pcbRead)
+ {
+ *pcbRead = cbRead;
+ if (rc == VINF_SUCCESS)
+ rc = rtVfsStdFile_ReadFixRC(pThis, off, cbSeg, cbReadSeg);
+ }
+ }
+
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSIOSTREAMOPS,pfnWrite}
+ */
+static DECLCALLBACK(int) rtVfsStdFile_Write(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbWritten)
+{
+ PRTVFSSTDFILE pThis = (PRTVFSSTDFILE)pvThis;
+ int rc;
+
+ NOREF(fBlocking);
+ if (pSgBuf->cSegs == 1)
+ {
+ if (off < 0)
+ rc = RTFileWrite(pThis->hFile, pSgBuf->paSegs[0].pvSeg, pSgBuf->paSegs[0].cbSeg, pcbWritten);
+ else
+ rc = RTFileWriteAt(pThis->hFile, off, pSgBuf->paSegs[0].pvSeg, pSgBuf->paSegs[0].cbSeg, pcbWritten);
+ }
+ else
+ {
+ size_t cbWritten = 0;
+ size_t cbWrittenSeg;
+ size_t *pcbWrittenSeg = pcbWritten ? &cbWrittenSeg : NULL;
+ rc = VINF_SUCCESS;
+
+ for (uint32_t iSeg = 0; iSeg < pSgBuf->cSegs; iSeg++)
+ {
+ void *pvSeg = pSgBuf->paSegs[iSeg].pvSeg;
+ size_t cbSeg = pSgBuf->paSegs[iSeg].cbSeg;
+
+ cbWrittenSeg = 0;
+ if (off < 0)
+ rc = RTFileWrite(pThis->hFile, pvSeg, cbSeg, pcbWrittenSeg);
+ else
+ {
+ rc = RTFileWriteAt(pThis->hFile, off, pvSeg, cbSeg, pcbWrittenSeg);
+ off += cbSeg;
+ }
+ if (RT_FAILURE(rc))
+ break;
+ if (pcbWritten)
+ {
+ cbWritten += cbWrittenSeg;
+ if (cbWrittenSeg != cbSeg)
+ break;
+ }
+ }
+
+ if (pcbWritten)
+ *pcbWritten = cbWritten;
+ }
+
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSIOSTREAMOPS,pfnFlush}
+ */
+static DECLCALLBACK(int) rtVfsStdFile_Flush(void *pvThis)
+{
+ PRTVFSSTDFILE pThis = (PRTVFSSTDFILE)pvThis;
+ int rc = RTFileFlush(pThis->hFile);
+#ifdef RT_OS_WINDOWS
+ /* Workaround for console handles. */ /** @todo push this further down? */
+ if ( rc == VERR_INVALID_HANDLE
+ && RTFileIsValid(pThis->hFile))
+ rc = VINF_NOT_SUPPORTED; /* not flushable */
+#endif
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSIOSTREAMOPS,pfnPollOne}
+ */
+static DECLCALLBACK(int) rtVfsStdFile_PollOne(void *pvThis, uint32_t fEvents, RTMSINTERVAL cMillies, bool fIntr,
+ uint32_t *pfRetEvents)
+{
+ NOREF(pvThis);
+ int rc;
+ if (fEvents != RTPOLL_EVT_ERROR)
+ {
+ *pfRetEvents = fEvents & ~RTPOLL_EVT_ERROR;
+ rc = VINF_SUCCESS;
+ }
+ else if (fIntr)
+ rc = RTThreadSleep(cMillies);
+ else
+ {
+ uint64_t uMsStart = RTTimeMilliTS();
+ do
+ rc = RTThreadSleep(cMillies);
+ while ( rc == VERR_INTERRUPTED
+ && !fIntr
+ && RTTimeMilliTS() - uMsStart < cMillies);
+ if (rc == VERR_INTERRUPTED)
+ rc = VERR_TIMEOUT;
+ }
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSIOSTREAMOPS,pfnTell}
+ */
+static DECLCALLBACK(int) rtVfsStdFile_Tell(void *pvThis, PRTFOFF poffActual)
+{
+ PRTVFSSTDFILE pThis = (PRTVFSSTDFILE)pvThis;
+ uint64_t offActual;
+ int rc = RTFileSeek(pThis->hFile, 0, RTFILE_SEEK_CURRENT, &offActual);
+ if (RT_SUCCESS(rc))
+ *poffActual = (RTFOFF)offActual;
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSIOSTREAMOPS,pfnSkip}
+ */
+static DECLCALLBACK(int) rtVfsStdFile_Skip(void *pvThis, RTFOFF cb)
+{
+ PRTVFSSTDFILE pThis = (PRTVFSSTDFILE)pvThis;
+ uint64_t offIgnore;
+ return RTFileSeek(pThis->hFile, cb, RTFILE_SEEK_CURRENT, &offIgnore);
+}
+
+
+/**
+ * @interface_method_impl{RTVFSOBJSETOPS,pfnMode}
+ */
+static DECLCALLBACK(int) rtVfsStdFile_SetMode(void *pvThis, RTFMODE fMode, RTFMODE fMask)
+{
+ PRTVFSSTDFILE pThis = (PRTVFSSTDFILE)pvThis;
+ if (fMask != ~RTFS_TYPE_MASK)
+ {
+#if 0
+ RTFMODE fCurMode;
+ int rc = RTFileGetMode(pThis->hFile, &fCurMode);
+ if (RT_FAILURE(rc))
+ return rc;
+ fMode |= ~fMask & fCurMode;
+#else
+ RTFSOBJINFO ObjInfo;
+ int rc = RTFileQueryInfo(pThis->hFile, &ObjInfo, RTFSOBJATTRADD_NOTHING);
+ if (RT_FAILURE(rc))
+ return rc;
+ fMode |= ~fMask & ObjInfo.Attr.fMode;
+#endif
+ }
+ return RTFileSetMode(pThis->hFile, fMode);
+}
+
+
+/**
+ * @interface_method_impl{RTVFSOBJSETOPS,pfnSetTimes}
+ */
+static DECLCALLBACK(int) rtVfsStdFile_SetTimes(void *pvThis, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
+ PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
+{
+ PRTVFSSTDFILE pThis = (PRTVFSSTDFILE)pvThis;
+ return RTFileSetTimes(pThis->hFile, pAccessTime, pModificationTime, pChangeTime, pBirthTime);
+}
+
+
+/**
+ * @interface_method_impl{RTVFSOBJSETOPS,pfnSetOwner}
+ */
+static DECLCALLBACK(int) rtVfsStdFile_SetOwner(void *pvThis, RTUID uid, RTGID gid)
+{
+#if 0
+ PRTVFSSTDFILE pThis = (PRTVFSSTDFILE)pvThis;
+ return RTFileSetOwner(pThis->hFile, uid, gid);
+#else
+ NOREF(pvThis); NOREF(uid); NOREF(gid);
+ return VERR_NOT_IMPLEMENTED;
+#endif
+}
+
+
+/**
+ * @interface_method_impl{RTVFSFILEOPS,pfnSeek}
+ */
+static DECLCALLBACK(int) rtVfsStdFile_Seek(void *pvThis, RTFOFF offSeek, unsigned uMethod, PRTFOFF poffActual)
+{
+ PRTVFSSTDFILE pThis = (PRTVFSSTDFILE)pvThis;
+ uint64_t offActual = 0;
+ int rc = RTFileSeek(pThis->hFile, offSeek, uMethod, &offActual);
+ if (RT_SUCCESS(rc))
+ *poffActual = offActual;
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSFILEOPS,pfnQuerySize}
+ */
+static DECLCALLBACK(int) rtVfsStdFile_QuerySize(void *pvThis, uint64_t *pcbFile)
+{
+ PRTVFSSTDFILE pThis = (PRTVFSSTDFILE)pvThis;
+ return RTFileGetSize(pThis->hFile, pcbFile);
+}
+
+
+/**
+ * @interface_method_impl{RTVFSFILEOPS,pfnSetSize}
+ */
+static DECLCALLBACK(int) rtVfsStdFile_SetSize(void *pvThis, uint64_t cbFile, uint32_t fFlags)
+{
+ PRTVFSSTDFILE pThis = (PRTVFSSTDFILE)pvThis;
+ switch (fFlags & RTVFSFILE_SIZE_F_ACTION_MASK)
+ {
+ case RTVFSFILE_SIZE_F_NORMAL:
+ return RTFileSetSize(pThis->hFile, cbFile);
+ case RTVFSFILE_SIZE_F_GROW:
+ return RTFileSetAllocationSize(pThis->hFile, cbFile, RTFILE_ALLOC_SIZE_F_DEFAULT);
+ case RTVFSFILE_SIZE_F_GROW_KEEP_SIZE:
+ return RTFileSetAllocationSize(pThis->hFile, cbFile, RTFILE_ALLOC_SIZE_F_KEEP_SIZE);
+ default:
+ return VERR_NOT_SUPPORTED;
+ }
+}
+
+
+/**
+ * @interface_method_impl{RTVFSFILEOPS,pfnQueryMaxSize}
+ */
+static DECLCALLBACK(int) rtVfsStdFile_QueryMaxSize(void *pvThis, uint64_t *pcbMax)
+{
+ PRTVFSSTDFILE pThis = (PRTVFSSTDFILE)pvThis;
+ RTFOFF cbMax = 0;
+ int rc = RTFileGetMaxSizeEx(pThis->hFile, &cbMax);
+ if (RT_SUCCESS(rc))
+ *pcbMax = cbMax;
+ return rc;
+}
+
+
+/**
+ * Standard file operations.
+ */
+DECL_HIDDEN_CONST(const RTVFSFILEOPS) g_rtVfsStdFileOps =
+{
+ { /* Stream */
+ { /* Obj */
+ RTVFSOBJOPS_VERSION,
+ RTVFSOBJTYPE_FILE,
+ "StdFile",
+ rtVfsStdFile_Close,
+ rtVfsStdFile_QueryInfo,
+ RTVFSOBJOPS_VERSION
+ },
+ RTVFSIOSTREAMOPS_VERSION,
+ 0,
+ rtVfsStdFile_Read,
+ rtVfsStdFile_Write,
+ rtVfsStdFile_Flush,
+ rtVfsStdFile_PollOne,
+ rtVfsStdFile_Tell,
+ rtVfsStdFile_Skip,
+ NULL /*ZeroFill*/,
+ RTVFSIOSTREAMOPS_VERSION,
+ },
+ RTVFSFILEOPS_VERSION,
+ 0,
+ { /* ObjSet */
+ RTVFSOBJSETOPS_VERSION,
+ RT_UOFFSETOF(RTVFSFILEOPS, ObjSet) - RT_UOFFSETOF(RTVFSFILEOPS, Stream.Obj),
+ rtVfsStdFile_SetMode,
+ rtVfsStdFile_SetTimes,
+ rtVfsStdFile_SetOwner,
+ RTVFSOBJSETOPS_VERSION
+ },
+ rtVfsStdFile_Seek,
+ rtVfsStdFile_QuerySize,
+ rtVfsStdFile_SetSize,
+ rtVfsStdFile_QueryMaxSize,
+ RTVFSFILEOPS_VERSION
+};
+
+
+/**
+ * Internal worker for RTVfsFileFromRTFile and RTVfsFileOpenNormal.
+ *
+ * @returns IRPT status code.
+ * @param hFile The IPRT file handle.
+ * @param fOpen The RTFILE_O_XXX flags.
+ * @param fLeaveOpen Whether to leave it open or close it.
+ * @param phVfsFile Where to return the handle.
+ */
+static int rtVfsFileFromRTFile(RTFILE hFile, uint64_t fOpen, bool fLeaveOpen, PRTVFSFILE phVfsFile)
+{
+ PRTVFSSTDFILE pThis;
+ RTVFSFILE hVfsFile;
+ int rc = RTVfsNewFile(&g_rtVfsStdFileOps, sizeof(RTVFSSTDFILE), fOpen, NIL_RTVFS, NIL_RTVFSLOCK,
+ &hVfsFile, (void **)&pThis);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ pThis->hFile = hFile;
+ pThis->fLeaveOpen = fLeaveOpen;
+ *phVfsFile = hVfsFile;
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTVfsFileFromRTFile(RTFILE hFile, uint64_t fOpen, bool fLeaveOpen, PRTVFSFILE phVfsFile)
+{
+ /*
+ * Check the handle validity.
+ */
+ RTFSOBJINFO ObjInfo;
+ int rc = RTFileQueryInfo(hFile, &ObjInfo, RTFSOBJATTRADD_NOTHING);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Set up some fake fOpen flags if necessary and create a VFS file handle.
+ */
+ if (!fOpen)
+ fOpen = RTFILE_O_READWRITE | RTFILE_O_DENY_NONE | RTFILE_O_OPEN_CREATE;
+
+ return rtVfsFileFromRTFile(hFile, fOpen, fLeaveOpen, phVfsFile);
+}
+
+
+RTDECL(int) RTVfsFileOpenNormal(const char *pszFilename, uint64_t fOpen, PRTVFSFILE phVfsFile)
+{
+ /*
+ * Open the file the normal way and pass it to RTVfsFileFromRTFile.
+ */
+ RTFILE hFile;
+ int rc = RTFileOpen(&hFile, pszFilename, fOpen);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Create a VFS file handle.
+ */
+ rc = rtVfsFileFromRTFile(hFile, fOpen, false /*fLeaveOpen*/, phVfsFile);
+ if (RT_FAILURE(rc))
+ RTFileClose(hFile);
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTVfsIoStrmFromRTFile(RTFILE hFile, uint64_t fOpen, bool fLeaveOpen, PRTVFSIOSTREAM phVfsIos)
+{
+ RTVFSFILE hVfsFile;
+ int rc = RTVfsFileFromRTFile(hFile, fOpen, fLeaveOpen, &hVfsFile);
+ if (RT_SUCCESS(rc))
+ {
+ *phVfsIos = RTVfsFileToIoStream(hVfsFile);
+ RTVfsFileRelease(hVfsFile);
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTVfsIoStrmOpenNormal(const char *pszFilename, uint64_t fOpen, PRTVFSIOSTREAM phVfsIos)
+{
+ RTVFSFILE hVfsFile;
+ int rc = RTVfsFileOpenNormal(pszFilename, fOpen, &hVfsFile);
+ if (RT_SUCCESS(rc))
+ {
+ *phVfsIos = RTVfsFileToIoStream(hVfsFile);
+ RTVfsFileRelease(hVfsFile);
+ }
+ return rc;
+}
+
+
+
+/**
+ * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnValidate}
+ */
+static DECLCALLBACK(int) rtVfsChainStdFile_Validate(PCRTVFSCHAINELEMENTREG pProviderReg, PRTVFSCHAINSPEC pSpec,
+ PRTVFSCHAINELEMSPEC pElement, uint32_t *poffError, PRTERRINFO pErrInfo)
+{
+ RT_NOREF(pProviderReg);
+
+ /*
+ * 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;
+
+ /*
+ * Join common cause with the 'open' provider.
+ */
+ return RTVfsChainValidateOpenFileOrIoStream(pSpec, pElement, poffError, pErrInfo);
+}
+
+
+/**
+ * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnInstantiate}
+ */
+static DECLCALLBACK(int) rtVfsChainStdFile_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);
+
+ RTVFSFILE hVfsFile;
+ int rc = RTVfsFileOpenNormal(pElement->paArgs[0].psz, pElement->uProvider, &hVfsFile);
+ if (RT_SUCCESS(rc))
+ {
+ *phVfsObj = RTVfsObjFromFile(hVfsFile);
+ RTVfsFileRelease(hVfsFile);
+ if (*phVfsObj != NIL_RTVFSOBJ)
+ return VINF_SUCCESS;
+ rc = VERR_VFS_CHAIN_CAST_FAILED;
+ }
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnCanReuseElement}
+ */
+static DECLCALLBACK(bool) rtVfsChainStdFile_CanReuseElement(PCRTVFSCHAINELEMENTREG pProviderReg,
+ PCRTVFSCHAINSPEC pSpec, PCRTVFSCHAINELEMSPEC pElement,
+ PCRTVFSCHAINSPEC pReuseSpec, PCRTVFSCHAINELEMSPEC pReuseElement)
+{
+ RT_NOREF(pProviderReg, pSpec, pReuseSpec);
+ if (strcmp(pElement->paArgs[0].psz, pReuseElement->paArgs[0].psz) == 0)
+ if (pElement->paArgs[0].uProvider == pReuseElement->paArgs[0].uProvider)
+ return true;
+ return false;
+}
+
+
+/** VFS chain element 'file'. */
+static RTVFSCHAINELEMENTREG g_rtVfsChainStdFileReg =
+{
+ /* uVersion = */ RTVFSCHAINELEMENTREG_VERSION,
+ /* fReserved = */ 0,
+ /* pszName = */ "stdfile",
+ /* ListEntry = */ { NULL, NULL },
+ /* pszHelp = */ "Open a real file, providing either a file or an I/O stream object. Initial element.\n"
+ "First argument is the filename path.\n"
+ "Second argument is access mode, optional: r, w, rw.\n"
+ "Third argument is open disposition, optional: create, create-replace, open, open-create, open-append, open-truncate.\n"
+ "Forth argument is file sharing, optional: nr, nw, nrw, d.",
+ /* pfnValidate = */ rtVfsChainStdFile_Validate,
+ /* pfnInstantiate = */ rtVfsChainStdFile_Instantiate,
+ /* pfnCanReuseElement = */ rtVfsChainStdFile_CanReuseElement,
+ /* uEndMarker = */ RTVFSCHAINELEMENTREG_VERSION
+};
+
+RTVFSCHAIN_AUTO_REGISTER_ELEMENT_PROVIDER(&g_rtVfsChainStdFileReg, rtVfsChainStdFileReg);
+