VirtualBox

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

Last change on this file since 98322 was 98103, checked in by vboxsync, 2 years ago

Copyright year updates by scm.

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