VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/vfs/vfsprogress.cpp@ 78052

Last change on this file since 78052 was 76553, checked in by vboxsync, 6 years ago

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.4 KB
Line 
1/* $Id: vfsprogress.cpp 76553 2019-01-01 01:45:53Z vboxsync $ */
2/** @file
3 * IPRT - Virtual File System, progress filter for files.
4 */
5
6/*
7 * Copyright (C) 2010-2019 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#include <iprt/vfs.h>
32#include <iprt/vfslowlevel.h>
33
34#include <iprt/assert.h>
35#include <iprt/errcore.h>
36#include <iprt/file.h>
37#include <iprt/poll.h>
38#include <iprt/string.h>
39#include <iprt/thread.h>
40
41
42/*********************************************************************************************************************************
43* Structures and Typedefs *
44*********************************************************************************************************************************/
45/**
46 * Private data of a standard file.
47 */
48typedef struct RTVFSPROGRESSFILE
49{
50 /** This is negative (RT_FAILURE) if canceled. */
51 int rcCanceled;
52 /** RTVFSPROGRESS_F_XXX. */
53 uint32_t fFlags;
54 /** Progress callback. */
55 PFNRTPROGRESS pfnProgress;
56 /** User argument for the callback. */
57 void *pvUser;
58 /** The I/O stream handle. */
59 RTVFSIOSTREAM hVfsIos;
60 /** The file handle. NIL_RTFILE if a pure I/O stream. */
61 RTVFSFILE hVfsFile;
62 /** Total number of bytes expected to be read and written. */
63 uint64_t cbExpected;
64 /** The number of bytes expected to be read. */
65 uint64_t cbExpectedRead;
66 /** The number of bytes expected to be written. */
67 uint64_t cbExpectedWritten;
68 /** Number of bytes currently read. */
69 uint64_t cbCurrentlyRead;
70 /** Number of bytes currently written. */
71 uint64_t cbCurrentlyWritten;
72 /** Current precentage. */
73 unsigned uCurPct;
74} RTVFSPROGRESSFILE;
75/** Pointer to the private data of a standard file. */
76typedef RTVFSPROGRESSFILE *PRTVFSPROGRESSFILE;
77
78
79/**
80 * Update the progress and do the progress callback if necessary.
81 *
82 * @returns Callback return code.
83 * @param pThis The file progress instance.
84 */
85static int rtVfsProgressFile_UpdateProgress(PRTVFSPROGRESSFILE pThis)
86{
87 uint64_t cbDone = RT_MIN(pThis->cbCurrentlyRead, pThis->cbExpectedRead)
88 + RT_MIN(pThis->cbCurrentlyWritten, pThis->cbExpectedWritten);
89 unsigned uPct = cbDone * 100 / pThis->cbExpected;
90 if (uPct == pThis->uCurPct)
91 return pThis->rcCanceled;
92 pThis->uCurPct = uPct;
93
94 int rc = pThis->pfnProgress(uPct, pThis->pvUser);
95 if (!(pThis->fFlags & RTVFSPROGRESS_F_CANCELABLE))
96 rc = VINF_SUCCESS;
97 else if (RT_FAILURE(rc) && RT_SUCCESS(pThis->rcCanceled))
98 pThis->rcCanceled = rc;
99
100 return rc;
101}
102
103
104/**
105 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
106 */
107static DECLCALLBACK(int) rtVfsProgressFile_Close(void *pvThis)
108{
109 PRTVFSPROGRESSFILE pThis = (PRTVFSPROGRESSFILE)pvThis;
110
111 if (pThis->hVfsFile != NIL_RTVFSFILE)
112 {
113 RTVfsFileRelease(pThis->hVfsFile);
114 pThis->hVfsFile = NIL_RTVFSFILE;
115 }
116 RTVfsIoStrmRelease(pThis->hVfsIos);
117 pThis->hVfsIos = NIL_RTVFSIOSTREAM;
118
119 pThis->pfnProgress = NULL;
120
121 return VINF_SUCCESS;
122}
123
124
125/**
126 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
127 */
128static DECLCALLBACK(int) rtVfsProgressFile_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
129{
130 PRTVFSPROGRESSFILE pThis = (PRTVFSPROGRESSFILE)pvThis;
131 int rc = pThis->rcCanceled;
132 if (RT_SUCCESS(rc))
133 rc = RTVfsIoStrmQueryInfo(pThis->hVfsIos, pObjInfo, enmAddAttr);
134 return rc;
135}
136
137
138/**
139 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnRead}
140 */
141static DECLCALLBACK(int) rtVfsProgressFile_Read(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbRead)
142{
143 PRTVFSPROGRESSFILE pThis = (PRTVFSPROGRESSFILE)pvThis;
144
145 int rc = pThis->rcCanceled;
146 if (RT_SUCCESS(rc))
147 {
148 /* Simplify a little there if a seeks is implied and assume the read goes well. */
149 if ( off >= 0
150 && (pThis->fFlags & RTVFSPROGRESS_F_FORWARD_SEEK_AS_READ))
151 {
152 uint64_t offCurrent = RTVfsFileTell(pThis->hVfsFile);
153 if (offCurrent < (uint64_t)off)
154 pThis->cbCurrentlyRead += off - offCurrent;
155 }
156
157 /* Calc the request before calling down the stack. */
158 size_t cbReq = 0;
159 unsigned i = pSgBuf->cSegs;
160 while (i-- > 0)
161 cbReq += pSgBuf->paSegs[i].cbSeg;
162
163 /* Do the read. */
164 rc = RTVfsIoStrmSgRead(pThis->hVfsIos, off, pSgBuf, fBlocking, pcbRead);
165 if (RT_SUCCESS(rc))
166 {
167 /* Update the progress (we cannot cancel here, sorry). */
168 pThis->cbCurrentlyRead += pcbRead ? *pcbRead : cbReq;
169 rtVfsProgressFile_UpdateProgress(pThis);
170 }
171 }
172
173 return rc;
174}
175
176
177/**
178 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnWrite}
179 */
180static DECLCALLBACK(int) rtVfsProgressFile_Write(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbWritten)
181{
182 PRTVFSPROGRESSFILE pThis = (PRTVFSPROGRESSFILE)pvThis;
183
184 int rc = pThis->rcCanceled;
185 if (RT_SUCCESS(rc))
186 {
187 /* Simplify a little there if a seeks is implied and assume the write goes well. */
188 if ( off >= 0
189 && (pThis->fFlags & RTVFSPROGRESS_F_FORWARD_SEEK_AS_WRITE))
190 {
191 uint64_t offCurrent = RTVfsFileTell(pThis->hVfsFile);
192 if (offCurrent < (uint64_t)off)
193 pThis->cbCurrentlyWritten += off - offCurrent;
194 }
195
196 /* Calc the request before calling down the stack. */
197 size_t cbReq = 0;
198 unsigned i = pSgBuf->cSegs;
199 while (i-- > 0)
200 cbReq += pSgBuf->paSegs[i].cbSeg;
201
202 /* Do the read. */
203 rc = RTVfsIoStrmSgWrite(pThis->hVfsIos, off, pSgBuf, fBlocking, pcbWritten);
204 if (RT_SUCCESS(rc))
205 {
206 /* Update the progress (we cannot cancel here, sorry). */
207 pThis->cbCurrentlyWritten += pcbWritten ? *pcbWritten : cbReq;
208 rtVfsProgressFile_UpdateProgress(pThis);
209 }
210 }
211
212 return rc;
213}
214
215
216/**
217 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnFlush}
218 */
219static DECLCALLBACK(int) rtVfsProgressFile_Flush(void *pvThis)
220{
221 PRTVFSPROGRESSFILE pThis = (PRTVFSPROGRESSFILE)pvThis;
222 int rc = pThis->rcCanceled;
223 if (RT_SUCCESS(rc))
224 rc = RTVfsIoStrmFlush(pThis->hVfsIos);
225 return rc;
226}
227
228
229/**
230 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnPollOne}
231 */
232static DECLCALLBACK(int)
233rtVfsProgressFile_PollOne(void *pvThis, uint32_t fEvents, RTMSINTERVAL cMillies, bool fIntr, uint32_t *pfRetEvents)
234{
235 PRTVFSPROGRESSFILE pThis = (PRTVFSPROGRESSFILE)pvThis;
236 int rc = pThis->rcCanceled;
237 if (RT_SUCCESS(rc))
238 rc = RTVfsIoStrmPoll(pThis->hVfsIos, fEvents, cMillies, fIntr, pfRetEvents);
239 else
240 {
241 *pfRetEvents |= RTPOLL_EVT_ERROR;
242 rc = VINF_SUCCESS;
243 }
244 return rc;
245}
246
247
248/**
249 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnTell}
250 */
251static DECLCALLBACK(int) rtVfsProgressFile_Tell(void *pvThis, PRTFOFF poffActual)
252{
253 PRTVFSPROGRESSFILE pThis = (PRTVFSPROGRESSFILE)pvThis;
254 *poffActual = RTVfsIoStrmTell(pThis->hVfsIos);
255 return *poffActual >= 0 ? VINF_SUCCESS : (int)*poffActual;
256}
257
258
259/**
260 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnSkip}
261 */
262static DECLCALLBACK(int) rtVfsProgressFile_Skip(void *pvThis, RTFOFF cb)
263{
264 PRTVFSPROGRESSFILE pThis = (PRTVFSPROGRESSFILE)pvThis;
265 int rc = pThis->rcCanceled;
266 if (RT_SUCCESS(rc))
267 {
268 rc = RTVfsIoStrmSkip(pThis->hVfsIos, cb);
269 if (RT_SUCCESS(rc))
270 {
271 pThis->cbCurrentlyRead += cb;
272 rtVfsProgressFile_UpdateProgress(pThis);
273 }
274 }
275 return rc;
276}
277
278
279/**
280 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnZeroFill}
281 */
282static DECLCALLBACK(int) rtVfsProgressFile_ZeroFill(void *pvThis, RTFOFF cb)
283{
284 PRTVFSPROGRESSFILE pThis = (PRTVFSPROGRESSFILE)pvThis;
285 int rc = pThis->rcCanceled;
286 if (RT_SUCCESS(rc))
287 {
288 rc = RTVfsIoStrmZeroFill(pThis->hVfsIos, cb);
289 if (RT_SUCCESS(rc))
290 {
291 pThis->cbCurrentlyWritten += cb;
292 rtVfsProgressFile_UpdateProgress(pThis);
293 }
294 }
295 return rc;
296}
297
298
299/**
300 * I/O stream progress operations.
301 */
302DECL_HIDDEN_CONST(const RTVFSIOSTREAMOPS) g_rtVfsProgressIosOps =
303{
304 { /* Obj */
305 RTVFSOBJOPS_VERSION,
306 RTVFSOBJTYPE_IO_STREAM,
307 "I/O Stream Progress",
308 rtVfsProgressFile_Close,
309 rtVfsProgressFile_QueryInfo,
310 RTVFSOBJOPS_VERSION
311 },
312 RTVFSIOSTREAMOPS_VERSION,
313 0,
314 rtVfsProgressFile_Read,
315 rtVfsProgressFile_Write,
316 rtVfsProgressFile_Flush,
317 rtVfsProgressFile_PollOne,
318 rtVfsProgressFile_Tell,
319 rtVfsProgressFile_Skip,
320 rtVfsProgressFile_ZeroFill,
321 RTVFSIOSTREAMOPS_VERSION,
322};
323
324
325
326/**
327 * @interface_method_impl{RTVFSOBJSETOPS,pfnMode}
328 */
329static DECLCALLBACK(int) rtVfsProgressFile_SetMode(void *pvThis, RTFMODE fMode, RTFMODE fMask)
330{
331 PRTVFSPROGRESSFILE pThis = (PRTVFSPROGRESSFILE)pvThis;
332 //return RTVfsFileSetMode(pThis->hVfsIos, fMode, fMask); - missing
333 RT_NOREF(pThis, fMode, fMask);
334 AssertFailedReturn(VERR_NOT_IMPLEMENTED);
335}
336
337
338/**
339 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetTimes}
340 */
341static DECLCALLBACK(int) rtVfsProgressFile_SetTimes(void *pvThis, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
342 PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
343{
344 PRTVFSPROGRESSFILE pThis = (PRTVFSPROGRESSFILE)pvThis;
345 //return RTVfsFileSetTimes(pThis->hVfsIos, pAccessTime, pModificationTime, pChangeTime, pBirthTime); - missing
346 RT_NOREF(pThis, pAccessTime, pModificationTime, pChangeTime, pBirthTime);
347 AssertFailedReturn(VERR_NOT_IMPLEMENTED);
348}
349
350
351/**
352 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetOwner}
353 */
354static DECLCALLBACK(int) rtVfsProgressFile_SetOwner(void *pvThis, RTUID uid, RTGID gid)
355{
356 PRTVFSPROGRESSFILE pThis = (PRTVFSPROGRESSFILE)pvThis;
357 //return RTVfsFileSetOwern(pThis->hVfsIos, uid, gid); - missing
358 RT_NOREF(pThis, uid, gid);
359 AssertFailedReturn(VERR_NOT_IMPLEMENTED);
360}
361
362
363/**
364 * @interface_method_impl{RTVFSFILEOPS,pfnSeek}
365 */
366static DECLCALLBACK(int) rtVfsProgressFile_Seek(void *pvThis, RTFOFF offSeek, unsigned uMethod, PRTFOFF poffActual)
367{
368 PRTVFSPROGRESSFILE pThis = (PRTVFSPROGRESSFILE)pvThis;
369
370 uint64_t offPrev = UINT64_MAX;
371 if (pThis->fFlags & (RTVFSPROGRESS_F_FORWARD_SEEK_AS_READ | RTVFSPROGRESS_F_FORWARD_SEEK_AS_WRITE))
372 offPrev = RTVfsFileTell(pThis->hVfsFile);
373
374 uint64_t offActual = 0;
375 int rc = RTVfsFileSeek(pThis->hVfsFile, offSeek, uMethod, &offActual);
376 if (RT_SUCCESS(rc))
377 {
378 if (poffActual)
379 *poffActual = offActual;
380
381 /* Do progress updates as requested. */
382 if (pThis->fFlags & (RTVFSPROGRESS_F_FORWARD_SEEK_AS_READ | RTVFSPROGRESS_F_FORWARD_SEEK_AS_WRITE))
383 {
384 if (offActual > offPrev)
385 {
386 if (pThis->fFlags & RTVFSPROGRESS_F_FORWARD_SEEK_AS_READ)
387 pThis->cbCurrentlyRead += offActual - offPrev;
388 else
389 pThis->cbCurrentlyWritten += offActual - offPrev;
390 rtVfsProgressFile_UpdateProgress(pThis);
391 }
392 }
393 }
394 return rc;
395}
396
397
398/**
399 * @interface_method_impl{RTVFSFILEOPS,pfnQuerySize}
400 */
401static DECLCALLBACK(int) rtVfsProgressFile_QuerySize(void *pvThis, uint64_t *pcbFile)
402{
403 PRTVFSPROGRESSFILE pThis = (PRTVFSPROGRESSFILE)pvThis;
404 return RTVfsFileGetSize(pThis->hVfsFile, pcbFile);
405}
406
407
408/**
409 * @interface_method_impl{RTVFSFILEOPS,pfnSetSize}
410 */
411static DECLCALLBACK(int) rtVfsProgressFile_SetSize(void *pvThis, uint64_t cbFile, uint32_t fFlags)
412{
413 PRTVFSPROGRESSFILE pThis = (PRTVFSPROGRESSFILE)pvThis;
414 return RTVfsFileSetSize(pThis->hVfsFile, cbFile, fFlags);
415}
416
417
418/**
419 * @interface_method_impl{RTVFSFILEOPS,pfnQueryMaxSize}
420 */
421static DECLCALLBACK(int) rtVfsProgressFile_QueryMaxSize(void *pvThis, uint64_t *pcbMax)
422{
423 PRTVFSPROGRESSFILE pThis = (PRTVFSPROGRESSFILE)pvThis;
424 return RTVfsFileQueryMaxSize(pThis->hVfsFile, pcbMax);
425}
426
427
428
429/**
430 * File progress operations.
431 */
432DECL_HIDDEN_CONST(const RTVFSFILEOPS) g_rtVfsProgressFileOps =
433{
434 { /* Stream */
435 { /* Obj */
436 RTVFSOBJOPS_VERSION,
437 RTVFSOBJTYPE_FILE,
438 "File Progress",
439 rtVfsProgressFile_Close,
440 rtVfsProgressFile_QueryInfo,
441 RTVFSOBJOPS_VERSION
442 },
443 RTVFSIOSTREAMOPS_VERSION,
444 0,
445 rtVfsProgressFile_Read,
446 rtVfsProgressFile_Write,
447 rtVfsProgressFile_Flush,
448 rtVfsProgressFile_PollOne,
449 rtVfsProgressFile_Tell,
450 rtVfsProgressFile_Skip,
451 rtVfsProgressFile_ZeroFill,
452 RTVFSIOSTREAMOPS_VERSION,
453 },
454 RTVFSFILEOPS_VERSION,
455 0,
456 { /* ObjSet */
457 RTVFSOBJSETOPS_VERSION,
458 RT_UOFFSETOF(RTVFSFILEOPS, ObjSet) - RT_UOFFSETOF(RTVFSFILEOPS, Stream.Obj),
459 rtVfsProgressFile_SetMode,
460 rtVfsProgressFile_SetTimes,
461 rtVfsProgressFile_SetOwner,
462 RTVFSOBJSETOPS_VERSION
463 },
464 rtVfsProgressFile_Seek,
465 rtVfsProgressFile_QuerySize,
466 rtVfsProgressFile_SetSize,
467 rtVfsProgressFile_QueryMaxSize,
468 RTVFSFILEOPS_VERSION
469};
470
471
472RTDECL(int) RTVfsCreateProgressForIoStream(RTVFSIOSTREAM hVfsIos, PFNRTPROGRESS pfnProgress, void *pvUser, uint32_t fFlags,
473 uint64_t cbExpectedRead, uint64_t cbExpectedWritten, PRTVFSIOSTREAM phVfsIos)
474{
475 AssertPtrReturn(pfnProgress, VERR_INVALID_POINTER);
476 AssertReturn(!(fFlags & ~RTVFSPROGRESS_F_VALID_MASK), VERR_INVALID_FLAGS);
477 AssertReturn(!(fFlags & RTVFSPROGRESS_F_FORWARD_SEEK_AS_READ) || !(fFlags & RTVFSPROGRESS_F_FORWARD_SEEK_AS_WRITE),
478 VERR_INVALID_FLAGS);
479
480 uint32_t cRefs = RTVfsIoStrmRetain(hVfsIos);
481 AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
482
483 PRTVFSPROGRESSFILE pThis;
484 int rc = RTVfsNewIoStream(&g_rtVfsProgressIosOps, sizeof(*pThis), RTVfsIoStrmGetOpenFlags(hVfsIos),
485 NIL_RTVFS, NIL_RTVFSLOCK, phVfsIos, (void **)&pThis);
486 if (RT_SUCCESS(rc))
487 {
488 pThis->rcCanceled = VINF_SUCCESS;
489 pThis->fFlags = fFlags;
490 pThis->pfnProgress = pfnProgress;
491 pThis->pvUser = pvUser;
492 pThis->hVfsIos = hVfsIos;
493 pThis->hVfsFile = RTVfsIoStrmToFile(hVfsIos);
494 pThis->cbCurrentlyRead = 0;
495 pThis->cbCurrentlyWritten = 0;
496 pThis->cbExpectedRead = cbExpectedRead;
497 pThis->cbExpectedWritten = cbExpectedWritten;
498 pThis->cbExpected = cbExpectedRead + cbExpectedWritten;
499 if (!pThis->cbExpected)
500 pThis->cbExpected = 1;
501 pThis->uCurPct = 0;
502 }
503 return rc;
504}
505
506
507RTDECL(int) RTVfsCreateProgressForFile(RTVFSFILE hVfsFile, PFNRTPROGRESS pfnProgress, void *pvUser, uint32_t fFlags,
508 uint64_t cbExpectedRead, uint64_t cbExpectedWritten, PRTVFSFILE phVfsFile)
509{
510 AssertPtrReturn(pfnProgress, VERR_INVALID_POINTER);
511 AssertReturn(!(fFlags & ~RTVFSPROGRESS_F_VALID_MASK), VERR_INVALID_FLAGS);
512 AssertReturn(!(fFlags & RTVFSPROGRESS_F_FORWARD_SEEK_AS_READ) || !(fFlags & RTVFSPROGRESS_F_FORWARD_SEEK_AS_WRITE),
513 VERR_INVALID_FLAGS);
514
515 uint32_t cRefs = RTVfsFileRetain(hVfsFile);
516 AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE);
517
518 RTVFSIOSTREAM hVfsIos = RTVfsFileToIoStream(hVfsFile);
519 AssertReturnStmt(hVfsIos != NIL_RTVFSIOSTREAM, RTVfsFileRelease(hVfsFile), VERR_INVALID_HANDLE);
520
521 PRTVFSPROGRESSFILE pThis;
522 int rc = RTVfsNewFile(&g_rtVfsProgressFileOps, sizeof(*pThis), RTVfsFileGetOpenFlags(hVfsFile),
523 NIL_RTVFS, NIL_RTVFSLOCK, phVfsFile, (void **)&pThis);
524 if (RT_SUCCESS(rc))
525 {
526 pThis->fFlags = fFlags;
527 pThis->pfnProgress = pfnProgress;
528 pThis->pvUser = pvUser;
529 pThis->hVfsIos = hVfsIos;
530 pThis->hVfsFile = hVfsFile;
531 pThis->cbCurrentlyRead = 0;
532 pThis->cbCurrentlyWritten = 0;
533 pThis->cbExpectedRead = cbExpectedRead;
534 pThis->cbExpectedWritten = cbExpectedWritten;
535 pThis->cbExpected = cbExpectedRead + cbExpectedWritten;
536 if (!pThis->cbExpected)
537 pThis->cbExpected = 1;
538 pThis->uCurPct = 0;
539 }
540 return rc;
541}
542
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette