diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:17:27 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:17:27 +0000 |
commit | f215e02bf85f68d3a6106c2a1f4f7f063f819064 (patch) | |
tree | 6bb5b92c046312c4e95ac2620b10ddf482d3fa8b /src/VBox/Runtime/common/vfs/vfsprogress.cpp | |
parent | Initial commit. (diff) | |
download | virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.tar.xz virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.zip |
Adding upstream version 7.0.14-dfsg.upstream/7.0.14-dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/Runtime/common/vfs/vfsprogress.cpp')
-rw-r--r-- | src/VBox/Runtime/common/vfs/vfsprogress.cpp | 554 |
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; +} + |