summaryrefslogtreecommitdiffstats
path: root/src/VBox/Runtime/common/vfs/vfsprogress.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Runtime/common/vfs/vfsprogress.cpp')
-rw-r--r--src/VBox/Runtime/common/vfs/vfsprogress.cpp554
1 files changed, 554 insertions, 0 deletions
diff --git a/src/VBox/Runtime/common/vfs/vfsprogress.cpp b/src/VBox/Runtime/common/vfs/vfsprogress.cpp
new file mode 100644
index 00000000..d6ef6533
--- /dev/null
+++ b/src/VBox/Runtime/common/vfs/vfsprogress.cpp
@@ -0,0 +1,554 @@
+/* $Id: vfsprogress.cpp $ */
+/** @file
+ * IPRT - Virtual File System, progress filter for files.
+ */
+
+/*
+ * Copyright (C) 2010-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 *
+*********************************************************************************************************************************/
+#include <iprt/vfs.h>
+#include <iprt/vfslowlevel.h>
+
+#include <iprt/assert.h>
+#include <iprt/errcore.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
+{
+ /** This is negative (RT_FAILURE) if canceled. */
+ int rcCanceled;
+ /** 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.
+ *
+ * @returns Callback return code.
+ * @param pThis The file progress instance.
+ */
+static int rtVfsProgressFile_UpdateProgress(PRTVFSPROGRESSFILE pThis)
+{
+ uint64_t cbDone = RT_MIN(pThis->cbCurrentlyRead, pThis->cbExpectedRead)
+ + RT_MIN(pThis->cbCurrentlyWritten, pThis->cbExpectedWritten);
+ unsigned uPct = cbDone * 100 / pThis->cbExpected;
+ if (uPct == pThis->uCurPct)
+ return pThis->rcCanceled;
+ pThis->uCurPct = uPct;
+
+ int rc = pThis->pfnProgress(uPct, pThis->pvUser);
+ if (!(pThis->fFlags & RTVFSPROGRESS_F_CANCELABLE))
+ rc = VINF_SUCCESS;
+ else if (RT_FAILURE(rc) && RT_SUCCESS(pThis->rcCanceled))
+ pThis->rcCanceled = rc;
+
+ return rc;
+}
+
+
+/**
+ * @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;
+ int rc = pThis->rcCanceled;
+ if (RT_SUCCESS(rc))
+ rc = RTVfsIoStrmQueryInfo(pThis->hVfsIos, pObjInfo, enmAddAttr);
+ return rc;
+}
+
+
+/**
+ * @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;
+
+ int rc = pThis->rcCanceled;
+ if (RT_SUCCESS(rc))
+ {
+ /* 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. */
+ 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;
+
+ int rc = pThis->rcCanceled;
+ if (RT_SUCCESS(rc))
+ {
+ /* 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. */
+ 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;
+ int rc = pThis->rcCanceled;
+ if (RT_SUCCESS(rc))
+ rc = RTVfsIoStrmFlush(pThis->hVfsIos);
+ return rc;
+}
+
+
+/**
+ * @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;
+ int rc = pThis->rcCanceled;
+ if (RT_SUCCESS(rc))
+ rc = RTVfsIoStrmPoll(pThis->hVfsIos, fEvents, cMillies, fIntr, pfRetEvents);
+ else
+ {
+ *pfRetEvents |= RTPOLL_EVT_ERROR;
+ rc = VINF_SUCCESS;
+ }
+ return rc;
+}
+
+
+/**
+ * @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 = pThis->rcCanceled;
+ if (RT_SUCCESS(rc))
+ {
+ 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 = pThis->rcCanceled;
+ if (RT_SUCCESS(rc))
+ {
+ 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,
+ NULL,
+ 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 RTVfsFileQuerySize(pThis->hVfsFile, pcbFile);
+}
+
+
+/**
+ * @interface_method_impl{RTVFSFILEOPS,pfnSetSize}
+ */
+static DECLCALLBACK(int) rtVfsProgressFile_SetSize(void *pvThis, uint64_t cbFile, uint32_t fFlags)
+{
+ PRTVFSPROGRESSFILE pThis = (PRTVFSPROGRESSFILE)pvThis;
+ return RTVfsFileSetSize(pThis->hVfsFile, cbFile, fFlags);
+}
+
+
+/**
+ * @interface_method_impl{RTVFSFILEOPS,pfnQueryMaxSize}
+ */
+static DECLCALLBACK(int) rtVfsProgressFile_QueryMaxSize(void *pvThis, uint64_t *pcbMax)
+{
+ PRTVFSPROGRESSFILE pThis = (PRTVFSPROGRESSFILE)pvThis;
+ return RTVfsFileQueryMaxSize(pThis->hVfsFile, pcbMax);
+}
+
+
+
+/**
+ * File progress operations.
+ */
+DECL_HIDDEN_CONST(const RTVFSFILEOPS) g_rtVfsProgressFileOps =
+{
+ { /* Stream */
+ { /* Obj */
+ RTVFSOBJOPS_VERSION,
+ RTVFSOBJTYPE_FILE,
+ "File Progress",
+ rtVfsProgressFile_Close,
+ rtVfsProgressFile_QueryInfo,
+ NULL,
+ 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_UOFFSETOF(RTVFSFILEOPS, ObjSet) - RT_UOFFSETOF(RTVFSFILEOPS, Stream.Obj),
+ rtVfsProgressFile_SetMode,
+ rtVfsProgressFile_SetTimes,
+ rtVfsProgressFile_SetOwner,
+ RTVFSOBJSETOPS_VERSION
+ },
+ rtVfsProgressFile_Seek,
+ rtVfsProgressFile_QuerySize,
+ rtVfsProgressFile_SetSize,
+ rtVfsProgressFile_QueryMaxSize,
+ RTVFSFILEOPS_VERSION
+};
+
+
+RTDECL(int) RTVfsCreateProgressForIoStream(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->rcCanceled = VINF_SUCCESS;
+ 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) RTVfsCreateProgressForFile(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;
+}
+