Index: trunk/src/VBox/Runtime/common/vfs/vfsbase.cpp
===================================================================
--- trunk/src/VBox/Runtime/common/vfs/vfsbase.cpp	(revision 67189)
+++ trunk/src/VBox/Runtime/common/vfs/vfsbase.cpp	(revision 67221)
@@ -3084,4 +3084,11 @@
 
 
+RTDECL(uint64_t) RTVfsIoStrmGetOpenFlags(RTVFSIOSTREAM hVfsIos)
+{
+    RTVFSIOSTREAMINTERNAL *pThis = hVfsIos;
+    AssertPtrReturn(pThis, 0);
+    AssertReturn(pThis->uMagic == RTVFSIOSTREAM_MAGIC, 0);
+    return pThis->fFlags;
+}
 
 
@@ -3405,2 +3412,12 @@
     return rc;
 }
+
+
+RTDECL(uint64_t) RTVfsFileGetOpenFlags(RTVFSFILE hVfsFile)
+{
+    RTVFSFILEINTERNAL *pThis = hVfsFile;
+    AssertPtrReturn(pThis, 0);
+    AssertReturn(pThis->uMagic == RTVFSFILE_MAGIC, 0);
+    return pThis->Stream.fFlags;
+}
+
Index: trunk/src/VBox/Runtime/common/vfs/vfsprogress.cpp
===================================================================
--- trunk/src/VBox/Runtime/common/vfs/vfsprogress.cpp	(revision 67221)
+++ trunk/src/VBox/Runtime/common/vfs/vfsprogress.cpp	(revision 67221)
@@ -0,0 +1,480 @@
+/* $Id$ */
+/** @file
+ * IPRT - Virtual File System, progress filter for files.
+ */
+
+/*
+ * Copyright (C) 2010-2017 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 RTVFSPROGRESSFILE
+{
+    /** RTVFSPROGRESS_F_XXX. */
+    uint32_t        fFlags;
+    /** Progress callback.   */
+    PFNRTPROGRESS   pfnProgress;
+    /** User argument for the callback. */
+    void           *pvUser;
+    /** The I/O stream handle. */
+    RTVFSIOSTREAM   hVfsIos;
+    /** The file handle.  NIL_RTFILE if a pure I/O stream. */
+    RTVFSFILE       hVfsFile;
+    /** Total number of bytes expected to be read and written. */
+    uint64_t        cbExpected;
+    /** The number of bytes expected to be read. */
+    uint64_t        cbExpectedRead;
+    /** The number of bytes expected to be written. */
+    uint64_t        cbExpectedWritten;
+    /** Number of bytes currently read. */
+    uint64_t        cbCurrentlyRead;
+    /** Number of bytes currently written. */
+    uint64_t        cbCurrentlyWritten;
+    /** Current precentage.   */
+    unsigned        uCurPct;
+} RTVFSPROGRESSFILE;
+/** Pointer to the private data of a standard file. */
+typedef RTVFSPROGRESSFILE *PRTVFSPROGRESSFILE;
+
+
+/**
+ * Update the progress and do the progress callback if necessary.
+ *
+ * @param   pThis     The file progress instance.
+ */
+static void rtVfsProgressFile_UpdateProgress(PRTVFSPROGRESSFILE pThis)
+{
+    uint64_t cbDone = RT_MIN(pThis->cbCurrentlyRead, pThis->cbExpectedRead)
+                    + RT_MIN(pThis->cbCurrentlyWritten, pThis->cbExpectedWritten);
+    unsigned uPct = cbDone / pThis->cbExpected;
+    if (uPct == pThis->uCurPct)
+        return;
+    pThis->uCurPct = uPct;
+    pThis->pfnProgress(uPct, pThis->pvUser);
+    /* Yes, we ignore the return code. */
+}
+
+
+/**
+ * @interface_method_impl{RTVFSOBJOPS,pfnClose}
+ */
+static DECLCALLBACK(int) rtVfsProgressFile_Close(void *pvThis)
+{
+    PRTVFSPROGRESSFILE pThis = (PRTVFSPROGRESSFILE)pvThis;
+
+    if (pThis->hVfsFile != NIL_RTVFSFILE)
+    {
+        RTVfsFileRelease(pThis->hVfsFile);
+        pThis->hVfsFile = NIL_RTVFSFILE;
+    }
+    RTVfsIoStrmRelease(pThis->hVfsIos);
+    pThis->hVfsIos = NIL_RTVFSIOSTREAM;
+
+    pThis->pfnProgress = NULL;
+
+    return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
+ */
+static DECLCALLBACK(int) rtVfsProgressFile_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
+{
+    PRTVFSPROGRESSFILE pThis = (PRTVFSPROGRESSFILE)pvThis;
+    return RTVfsIoStrmQueryInfo(pThis->hVfsIos, pObjInfo, enmAddAttr);
+}
+
+
+/**
+ * @interface_method_impl{RTVFSIOSTREAMOPS,pfnRead}
+ */
+static DECLCALLBACK(int) rtVfsProgressFile_Read(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbRead)
+{
+    PRTVFSPROGRESSFILE pThis = (PRTVFSPROGRESSFILE)pvThis;
+
+    /* Simplify a little there if a seeks is implied and assume the read goes well. */
+    if (   off >= 0
+        && (pThis->fFlags & RTVFSPROGRESS_F_FORWARD_SEEK_AS_READ))
+    {
+        uint64_t offCurrent = RTVfsFileTell(pThis->hVfsFile);
+        if (offCurrent < (uint64_t)off)
+            pThis->cbCurrentlyRead += off - offCurrent;
+    }
+
+    /* Calc the request before calling down the stack. */
+    size_t   cbReq = 0;
+    unsigned i = pSgBuf->cSegs;
+    while (i-- > 0)
+        cbReq += pSgBuf->paSegs[i].cbSeg;
+
+    /* Do the read. */
+    int rc = RTVfsIoStrmSgRead(pThis->hVfsIos, off, pSgBuf, fBlocking, pcbRead);
+    if (RT_SUCCESS(rc))
+    {
+        /* Update the progress (we cannot cancel here, sorry). */
+        pThis->cbCurrentlyRead += pcbRead ? *pcbRead : cbReq;
+        rtVfsProgressFile_UpdateProgress(pThis);
+    }
+
+    return rc;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSIOSTREAMOPS,pfnWrite}
+ */
+static DECLCALLBACK(int) rtVfsProgressFile_Write(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbWritten)
+{
+    PRTVFSPROGRESSFILE pThis = (PRTVFSPROGRESSFILE)pvThis;
+
+    /* Simplify a little there if a seeks is implied and assume the write goes well. */
+    if (   off >= 0
+        && (pThis->fFlags & RTVFSPROGRESS_F_FORWARD_SEEK_AS_WRITE))
+    {
+        uint64_t offCurrent = RTVfsFileTell(pThis->hVfsFile);
+        if (offCurrent < (uint64_t)off)
+            pThis->cbCurrentlyWritten += off - offCurrent;
+    }
+
+    /* Calc the request before calling down the stack. */
+    size_t   cbReq = 0;
+    unsigned i = pSgBuf->cSegs;
+    while (i-- > 0)
+        cbReq += pSgBuf->paSegs[i].cbSeg;
+
+    /* Do the read. */
+    int rc = RTVfsIoStrmSgWrite(pThis->hVfsIos, off, pSgBuf, fBlocking, pcbWritten);
+    if (RT_SUCCESS(rc))
+    {
+        /* Update the progress (we cannot cancel here, sorry). */
+        pThis->cbCurrentlyWritten += pcbWritten ? *pcbWritten : cbReq;
+        rtVfsProgressFile_UpdateProgress(pThis);
+    }
+
+    return rc;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSIOSTREAMOPS,pfnFlush}
+ */
+static DECLCALLBACK(int) rtVfsProgressFile_Flush(void *pvThis)
+{
+    PRTVFSPROGRESSFILE pThis = (PRTVFSPROGRESSFILE)pvThis;
+    return RTVfsIoStrmFlush(pThis->hVfsIos);
+}
+
+
+/**
+ * @interface_method_impl{RTVFSIOSTREAMOPS,pfnPollOne}
+ */
+static DECLCALLBACK(int)
+rtVfsProgressFile_PollOne(void *pvThis, uint32_t fEvents, RTMSINTERVAL cMillies, bool fIntr, uint32_t *pfRetEvents)
+{
+    PRTVFSPROGRESSFILE pThis = (PRTVFSPROGRESSFILE)pvThis;
+    return RTVfsIoStrmPoll(pThis->hVfsIos, fEvents, cMillies, fIntr, pfRetEvents);
+}
+
+
+/**
+ * @interface_method_impl{RTVFSIOSTREAMOPS,pfnTell}
+ */
+static DECLCALLBACK(int) rtVfsProgressFile_Tell(void *pvThis, PRTFOFF poffActual)
+{
+    PRTVFSPROGRESSFILE pThis = (PRTVFSPROGRESSFILE)pvThis;
+    *poffActual = RTVfsIoStrmTell(pThis->hVfsIos);
+    return *poffActual >= 0 ? VINF_SUCCESS : (int)*poffActual;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSIOSTREAMOPS,pfnSkip}
+ */
+static DECLCALLBACK(int) rtVfsProgressFile_Skip(void *pvThis, RTFOFF cb)
+{
+    PRTVFSPROGRESSFILE pThis = (PRTVFSPROGRESSFILE)pvThis;
+    int rc = RTVfsIoStrmSkip(pThis->hVfsIos, cb);
+    if (RT_SUCCESS(rc))
+    {
+        pThis->cbCurrentlyRead += cb;
+        rtVfsProgressFile_UpdateProgress(pThis);
+    }
+    return rc;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSIOSTREAMOPS,pfnZeroFill}
+ */
+static DECLCALLBACK(int) rtVfsProgressFile_ZeroFill(void *pvThis, RTFOFF cb)
+{
+    PRTVFSPROGRESSFILE pThis = (PRTVFSPROGRESSFILE)pvThis;
+    int rc = RTVfsIoStrmZeroFill(pThis->hVfsIos, cb);
+    if (RT_SUCCESS(rc))
+    {
+        pThis->cbCurrentlyWritten += cb;
+        rtVfsProgressFile_UpdateProgress(pThis);
+    }
+    return rc;
+}
+
+
+/**
+ * I/O stream progress operations.
+ */
+DECL_HIDDEN_CONST(const RTVFSIOSTREAMOPS) g_rtVfsProgressIosOps =
+{
+    { /* Obj */
+        RTVFSOBJOPS_VERSION,
+        RTVFSOBJTYPE_IO_STREAM,
+        "I/O Stream Progress",
+        rtVfsProgressFile_Close,
+        rtVfsProgressFile_QueryInfo,
+        RTVFSOBJOPS_VERSION
+    },
+    RTVFSIOSTREAMOPS_VERSION,
+    0,
+    rtVfsProgressFile_Read,
+    rtVfsProgressFile_Write,
+    rtVfsProgressFile_Flush,
+    rtVfsProgressFile_PollOne,
+    rtVfsProgressFile_Tell,
+    rtVfsProgressFile_Skip,
+    rtVfsProgressFile_ZeroFill,
+    RTVFSIOSTREAMOPS_VERSION,
+};
+
+
+
+/**
+ * @interface_method_impl{RTVFSOBJSETOPS,pfnMode}
+ */
+static DECLCALLBACK(int) rtVfsProgressFile_SetMode(void *pvThis, RTFMODE fMode, RTFMODE fMask)
+{
+    PRTVFSPROGRESSFILE pThis = (PRTVFSPROGRESSFILE)pvThis;
+    //return RTVfsFileSetMode(pThis->hVfsIos, fMode, fMask); - missing
+    RT_NOREF(pThis, fMode, fMask);
+    AssertFailedReturn(VERR_NOT_IMPLEMENTED);
+}
+
+
+/**
+ * @interface_method_impl{RTVFSOBJSETOPS,pfnSetTimes}
+ */
+static DECLCALLBACK(int) rtVfsProgressFile_SetTimes(void *pvThis, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
+                                                    PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
+{
+    PRTVFSPROGRESSFILE pThis = (PRTVFSPROGRESSFILE)pvThis;
+    //return RTVfsFileSetTimes(pThis->hVfsIos, pAccessTime, pModificationTime, pChangeTime, pBirthTime); - missing
+    RT_NOREF(pThis, pAccessTime, pModificationTime, pChangeTime, pBirthTime);
+    AssertFailedReturn(VERR_NOT_IMPLEMENTED);
+}
+
+
+/**
+ * @interface_method_impl{RTVFSOBJSETOPS,pfnSetOwner}
+ */
+static DECLCALLBACK(int) rtVfsProgressFile_SetOwner(void *pvThis, RTUID uid, RTGID gid)
+{
+    PRTVFSPROGRESSFILE pThis = (PRTVFSPROGRESSFILE)pvThis;
+    //return RTVfsFileSetOwern(pThis->hVfsIos, uid, gid); - missing
+    RT_NOREF(pThis, uid, gid);
+    AssertFailedReturn(VERR_NOT_IMPLEMENTED);
+}
+
+
+/**
+ * @interface_method_impl{RTVFSFILEOPS,pfnSeek}
+ */
+static DECLCALLBACK(int) rtVfsProgressFile_Seek(void *pvThis, RTFOFF offSeek, unsigned uMethod, PRTFOFF poffActual)
+{
+    PRTVFSPROGRESSFILE pThis = (PRTVFSPROGRESSFILE)pvThis;
+
+    uint64_t offPrev = UINT64_MAX;
+    if (pThis->fFlags & (RTVFSPROGRESS_F_FORWARD_SEEK_AS_READ | RTVFSPROGRESS_F_FORWARD_SEEK_AS_WRITE))
+        offPrev = RTVfsFileTell(pThis->hVfsFile);
+
+    uint64_t offActual = 0;
+    int rc = RTVfsFileSeek(pThis->hVfsFile, offSeek, uMethod, &offActual);
+    if (RT_SUCCESS(rc))
+    {
+        if (poffActual)
+            *poffActual = offActual;
+
+        /* Do progress updates as requested. */
+        if (pThis->fFlags & (RTVFSPROGRESS_F_FORWARD_SEEK_AS_READ | RTVFSPROGRESS_F_FORWARD_SEEK_AS_WRITE))
+        {
+            if (offActual > offPrev)
+            {
+                if (pThis->fFlags & RTVFSPROGRESS_F_FORWARD_SEEK_AS_READ)
+                    pThis->cbCurrentlyRead += offActual - offPrev;
+                else
+                    pThis->cbCurrentlyWritten += offActual - offPrev;
+                rtVfsProgressFile_UpdateProgress(pThis);
+            }
+        }
+    }
+    return rc;
+}
+
+
+/**
+ * @interface_method_impl{RTVFSFILEOPS,pfnQuerySize}
+ */
+static DECLCALLBACK(int) rtVfsProgressFile_QuerySize(void *pvThis, uint64_t *pcbFile)
+{
+    PRTVFSPROGRESSFILE pThis = (PRTVFSPROGRESSFILE)pvThis;
+    return RTVfsFileGetSize(pThis->hVfsFile, pcbFile);
+}
+
+
+/**
+ * File progress operations.
+ */
+DECL_HIDDEN_CONST(const RTVFSFILEOPS) g_rtVfsProgressFileOps =
+{
+    { /* Stream */
+        { /* Obj */
+            RTVFSOBJOPS_VERSION,
+            RTVFSOBJTYPE_FILE,
+            "File Progress",
+            rtVfsProgressFile_Close,
+            rtVfsProgressFile_QueryInfo,
+            RTVFSOBJOPS_VERSION
+        },
+        RTVFSIOSTREAMOPS_VERSION,
+        0,
+        rtVfsProgressFile_Read,
+        rtVfsProgressFile_Write,
+        rtVfsProgressFile_Flush,
+        rtVfsProgressFile_PollOne,
+        rtVfsProgressFile_Tell,
+        rtVfsProgressFile_Skip,
+        rtVfsProgressFile_ZeroFill,
+        RTVFSIOSTREAMOPS_VERSION,
+    },
+    RTVFSFILEOPS_VERSION,
+    0,
+    { /* ObjSet */
+        RTVFSOBJSETOPS_VERSION,
+        RT_OFFSETOF(RTVFSFILEOPS, Stream.Obj) - RT_OFFSETOF(RTVFSFILEOPS, ObjSet),
+        rtVfsProgressFile_SetMode,
+        rtVfsProgressFile_SetTimes,
+        rtVfsProgressFile_SetOwner,
+        RTVFSOBJSETOPS_VERSION
+    },
+    rtVfsProgressFile_Seek,
+    rtVfsProgressFile_QuerySize,
+    RTVFSFILEOPS_VERSION
+};
+
+
+RTDECL(int) RTVfsIoStrmCreateProgress(RTVFSIOSTREAM hVfsIos, PFNRTPROGRESS pfnProgress, void *pvUser, uint32_t fFlags,
+                                      uint64_t cbExpectedRead, uint64_t cbExpectedWritten, PRTVFSIOSTREAM phVfsIos)
+{
+    AssertPtrReturn(pfnProgress, VERR_INVALID_POINTER);
+    AssertReturn(!(fFlags & ~RTVFSPROGRESS_F_VALID_MASK), VERR_INVALID_FLAGS);
+    AssertReturn(!(fFlags & RTVFSPROGRESS_F_FORWARD_SEEK_AS_READ) || !(fFlags & RTVFSPROGRESS_F_FORWARD_SEEK_AS_WRITE),
+                 VERR_INVALID_FLAGS);
+
+    uint32_t cRefs = RTVfsIoStrmRetain(hVfsIos);
+    AssertReturn(cRefs == UINT32_MAX, VERR_INVALID_HANDLE);
+
+    PRTVFSPROGRESSFILE pThis;
+    int rc = RTVfsNewIoStream(&g_rtVfsProgressIosOps, sizeof(*pThis), RTVfsIoStrmGetOpenFlags(hVfsIos),
+                              NIL_RTVFS, NIL_RTVFSLOCK, phVfsIos, (void **)&pThis);
+    if (RT_SUCCESS(rc))
+    {
+        pThis->fFlags               = fFlags;
+        pThis->pfnProgress          = pfnProgress;
+        pThis->pvUser               = pvUser;
+        pThis->hVfsIos              = hVfsIos;
+        pThis->hVfsFile             = RTVfsIoStrmToFile(hVfsIos);
+        pThis->cbCurrentlyRead      = 0;
+        pThis->cbCurrentlyWritten   = 0;
+        pThis->cbExpectedRead       = cbExpectedRead;
+        pThis->cbExpectedWritten    = cbExpectedWritten;
+        pThis->cbExpected           = cbExpectedRead + cbExpectedWritten;
+        if (!pThis->cbExpected)
+            pThis->cbExpected       = 1;
+        pThis->uCurPct              = 0;
+    }
+    return rc;
+}
+
+
+RTDECL(int) RTVfsFileCreateProgress(RTVFSFILE hVfsFile, PFNRTPROGRESS pfnProgress, void *pvUser, uint32_t fFlags,
+                                    uint64_t cbExpectedRead, uint64_t cbExpectedWritten, PRTVFSFILE phVfsFile)
+{
+    AssertPtrReturn(pfnProgress, VERR_INVALID_POINTER);
+    AssertReturn(!(fFlags & ~RTVFSPROGRESS_F_VALID_MASK), VERR_INVALID_FLAGS);
+    AssertReturn(!(fFlags & RTVFSPROGRESS_F_FORWARD_SEEK_AS_READ) || !(fFlags & RTVFSPROGRESS_F_FORWARD_SEEK_AS_WRITE),
+                 VERR_INVALID_FLAGS);
+
+    uint32_t cRefs = RTVfsFileRetain(hVfsFile);
+    AssertReturn(cRefs == UINT32_MAX, VERR_INVALID_HANDLE);
+
+    RTVFSIOSTREAM hVfsIos = RTVfsFileToIoStream(hVfsFile);
+    AssertReturnStmt(hVfsIos != NIL_RTVFSIOSTREAM, RTVfsFileRelease(hVfsFile), VERR_INVALID_HANDLE);
+
+    PRTVFSPROGRESSFILE pThis;
+    int rc = RTVfsNewFile(&g_rtVfsProgressFileOps, sizeof(*pThis), RTVfsFileGetOpenFlags(hVfsFile),
+                          NIL_RTVFS, NIL_RTVFSLOCK, phVfsFile, (void **)&pThis);
+    if (RT_SUCCESS(rc))
+    {
+        pThis->fFlags               = fFlags;
+        pThis->pfnProgress          = pfnProgress;
+        pThis->pvUser               = pvUser;
+        pThis->hVfsIos              = hVfsIos;
+        pThis->hVfsFile             = hVfsFile;
+        pThis->cbCurrentlyRead      = 0;
+        pThis->cbCurrentlyWritten   = 0;
+        pThis->cbExpectedRead       = cbExpectedRead;
+        pThis->cbExpectedWritten    = cbExpectedWritten;
+        pThis->cbExpected           = cbExpectedRead + cbExpectedWritten;
+        if (!pThis->cbExpected)
+            pThis->cbExpected       = 1;
+        pThis->uCurPct              = 0;
+    }
+    return rc;
+}
+
+
