[1] | 1 | /* $Id: fileio.cpp 99758 2023-05-11 21:37:59Z vboxsync $ */
|
---|
| 2 | /** @file
|
---|
[8245] | 3 | * IPRT - File I/O.
|
---|
[1] | 4 | */
|
---|
| 5 |
|
---|
| 6 | /*
|
---|
[98103] | 7 | * Copyright (C) 2006-2023 Oracle and/or its affiliates.
|
---|
[1] | 8 | *
|
---|
[96407] | 9 | * This file is part of VirtualBox base platform packages, as
|
---|
| 10 | * available from https://www.virtualbox.org.
|
---|
[5999] | 11 | *
|
---|
[96407] | 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 | *
|
---|
[5999] | 25 | * The contents of this file may alternatively be used under the terms
|
---|
| 26 | * of the Common Development and Distribution License Version 1.0
|
---|
[96407] | 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
|
---|
[5999] | 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.
|
---|
[96407] | 33 | *
|
---|
| 34 | * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
|
---|
[1] | 35 | */
|
---|
| 36 |
|
---|
[57358] | 37 |
|
---|
| 38 | /*********************************************************************************************************************************
|
---|
| 39 | * Header Files *
|
---|
| 40 | *********************************************************************************************************************************/
|
---|
[49033] | 41 | #include "internal/iprt.h"
|
---|
[1] | 42 | #include <iprt/file.h>
|
---|
[49033] | 43 |
|
---|
| 44 | #include <iprt/mem.h>
|
---|
[1] | 45 | #include <iprt/assert.h>
|
---|
| 46 | #include <iprt/alloca.h>
|
---|
[49033] | 47 | #include <iprt/string.h>
|
---|
[1] | 48 | #include <iprt/err.h>
|
---|
| 49 | #include "internal/file.h"
|
---|
| 50 |
|
---|
| 51 |
|
---|
[57358] | 52 | /*********************************************************************************************************************************
|
---|
| 53 | * Global Variables *
|
---|
| 54 | *********************************************************************************************************************************/
|
---|
[1] | 55 | /** Set of forced set open flags for files opened read-only. */
|
---|
| 56 | static unsigned g_fOpenReadSet = 0;
|
---|
| 57 |
|
---|
| 58 | /** Set of forced cleared open flags for files opened read-only. */
|
---|
| 59 | static unsigned g_fOpenReadMask = 0;
|
---|
| 60 |
|
---|
| 61 | /** Set of forced set open flags for files opened write-only. */
|
---|
| 62 | static unsigned g_fOpenWriteSet = 0;
|
---|
| 63 |
|
---|
| 64 | /** Set of forced cleared open flags for files opened write-only. */
|
---|
| 65 | static unsigned g_fOpenWriteMask = 0;
|
---|
| 66 |
|
---|
| 67 | /** Set of forced set open flags for files opened read-write. */
|
---|
| 68 | static unsigned g_fOpenReadWriteSet = 0;
|
---|
| 69 |
|
---|
| 70 | /** Set of forced cleared open flags for files opened read-write. */
|
---|
| 71 | static unsigned g_fOpenReadWriteMask = 0;
|
---|
| 72 |
|
---|
| 73 |
|
---|
| 74 | /**
|
---|
| 75 | * Force the use of open flags for all files opened after the setting is
|
---|
| 76 | * changed. The caller is responsible for not causing races with RTFileOpen().
|
---|
| 77 | *
|
---|
| 78 | * @returns iprt status code.
|
---|
| 79 | * @param fOpenForAccess Access mode to which the set/mask settings apply.
|
---|
| 80 | * @param fSet Open flags to be forced set.
|
---|
| 81 | * @param fMask Open flags to be masked out.
|
---|
| 82 | */
|
---|
| 83 | RTR3DECL(int) RTFileSetForceFlags(unsigned fOpenForAccess, unsigned fSet, unsigned fMask)
|
---|
| 84 | {
|
---|
| 85 | /*
|
---|
| 86 | * For now allow only RTFILE_O_WRITE_THROUGH. The other flags either
|
---|
| 87 | * make no sense in this context or are not useful to apply to all files.
|
---|
| 88 | */
|
---|
| 89 | if ((fSet | fMask) & ~RTFILE_O_WRITE_THROUGH)
|
---|
| 90 | return VERR_INVALID_PARAMETER;
|
---|
| 91 | switch (fOpenForAccess)
|
---|
| 92 | {
|
---|
| 93 | case RTFILE_O_READ:
|
---|
| 94 | g_fOpenReadSet = fSet;
|
---|
| 95 | g_fOpenReadMask = fMask;
|
---|
| 96 | break;
|
---|
| 97 | case RTFILE_O_WRITE:
|
---|
| 98 | g_fOpenWriteSet = fSet;
|
---|
| 99 | g_fOpenWriteMask = fMask;
|
---|
| 100 | break;
|
---|
| 101 | case RTFILE_O_READWRITE:
|
---|
| 102 | g_fOpenReadWriteSet = fSet;
|
---|
| 103 | g_fOpenReadWriteMask = fMask;
|
---|
| 104 | break;
|
---|
| 105 | default:
|
---|
| 106 | AssertMsgFailed(("Invalid access mode %d\n", fOpenForAccess));
|
---|
| 107 | return VERR_INVALID_PARAMETER;
|
---|
| 108 | }
|
---|
| 109 | return VINF_SUCCESS;
|
---|
| 110 | }
|
---|
| 111 |
|
---|
| 112 |
|
---|
| 113 | /**
|
---|
| 114 | * Adjusts and validates the flags.
|
---|
| 115 | *
|
---|
| 116 | * The adjustments are made according to the wishes specified using the RTFileSetForceFlags API.
|
---|
| 117 | *
|
---|
| 118 | * @returns IPRT status code.
|
---|
| 119 | * @param pfOpen Pointer to the user specified flags on input.
|
---|
| 120 | * Updated on successful return.
|
---|
| 121 | * @internal
|
---|
| 122 | */
|
---|
[37596] | 123 | int rtFileRecalcAndValidateFlags(uint64_t *pfOpen)
|
---|
[1] | 124 | {
|
---|
| 125 | /*
|
---|
| 126 | * Recalc.
|
---|
| 127 | */
|
---|
[23973] | 128 | uint32_t fOpen = *pfOpen;
|
---|
[1] | 129 | switch (fOpen & RTFILE_O_ACCESS_MASK)
|
---|
| 130 | {
|
---|
| 131 | case RTFILE_O_READ:
|
---|
| 132 | fOpen |= g_fOpenReadSet;
|
---|
| 133 | fOpen &= ~g_fOpenReadMask;
|
---|
| 134 | break;
|
---|
| 135 | case RTFILE_O_WRITE:
|
---|
| 136 | fOpen |= g_fOpenWriteSet;
|
---|
| 137 | fOpen &= ~g_fOpenWriteMask;
|
---|
| 138 | break;
|
---|
| 139 | case RTFILE_O_READWRITE:
|
---|
| 140 | fOpen |= g_fOpenReadWriteSet;
|
---|
| 141 | fOpen &= ~g_fOpenReadWriteMask;
|
---|
| 142 | break;
|
---|
[58769] | 143 | #ifdef RT_OS_WINDOWS
|
---|
| 144 | case RTFILE_O_ATTR_ONLY:
|
---|
| 145 | if (fOpen & RTFILE_O_ACCESS_ATTR_MASK)
|
---|
| 146 | break;
|
---|
| 147 | #endif
|
---|
[1] | 148 | default:
|
---|
[58769] | 149 | AssertMsgFailed(("Invalid access mode value, fOpen=%#llx\n", fOpen));
|
---|
[1] | 150 | return VERR_INVALID_PARAMETER;
|
---|
| 151 | }
|
---|
| 152 |
|
---|
| 153 | /*
|
---|
| 154 | * Validate .
|
---|
| 155 | */
|
---|
[58825] | 156 | #ifdef RT_OS_WINDOWS
|
---|
[58826] | 157 | AssertMsgReturn((fOpen & RTFILE_O_ACCESS_MASK) || (fOpen & RTFILE_O_ACCESS_ATTR_MASK),
|
---|
| 158 | ("Missing RTFILE_O_READ/WRITE/ACCESS_ATTR: fOpen=%#llx\n", fOpen), VERR_INVALID_PARAMETER);
|
---|
[58825] | 159 | #else
|
---|
[37596] | 160 | AssertMsgReturn(fOpen & RTFILE_O_ACCESS_MASK, ("Missing RTFILE_O_READ/WRITE: fOpen=%#llx\n", fOpen), VERR_INVALID_PARAMETER);
|
---|
[58825] | 161 | #endif
|
---|
[5356] | 162 | #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
|
---|
[37596] | 163 | AssertMsgReturn(!(fOpen & (~(uint64_t)RTFILE_O_VALID_MASK | RTFILE_O_NON_BLOCK)), ("%#llx\n", fOpen), VERR_INVALID_PARAMETER);
|
---|
[5356] | 164 | #else
|
---|
[37596] | 165 | AssertMsgReturn(!(fOpen & ~(uint64_t)RTFILE_O_VALID_MASK), ("%#llx\n", fOpen), VERR_INVALID_PARAMETER);
|
---|
[5356] | 166 | #endif
|
---|
[37596] | 167 | AssertMsgReturn((fOpen & (RTFILE_O_TRUNCATE | RTFILE_O_WRITE)) != RTFILE_O_TRUNCATE, ("%#llx\n", fOpen), VERR_INVALID_PARAMETER);
|
---|
[23973] | 168 |
|
---|
| 169 | switch (fOpen & RTFILE_O_ACTION_MASK)
|
---|
[1] | 170 | {
|
---|
[23973] | 171 | case 0: /* temporarily */
|
---|
| 172 | AssertMsgFailed(("Missing RTFILE_O_OPEN/CREATE*! (continuable assertion)\n"));
|
---|
| 173 | fOpen |= RTFILE_O_OPEN;
|
---|
| 174 | break;
|
---|
| 175 | case RTFILE_O_OPEN:
|
---|
[37596] | 176 | AssertMsgReturn(!(RTFILE_O_NOT_CONTENT_INDEXED & fOpen), ("%#llx\n", fOpen), VERR_INVALID_PARAMETER);
|
---|
[23973] | 177 | case RTFILE_O_OPEN_CREATE:
|
---|
| 178 | case RTFILE_O_CREATE:
|
---|
| 179 | case RTFILE_O_CREATE_REPLACE:
|
---|
| 180 | break;
|
---|
| 181 | default:
|
---|
[37596] | 182 | AssertMsgFailed(("Invalid action value: fOpen=%#llx\n", fOpen));
|
---|
[23973] | 183 | return VERR_INVALID_PARAMETER;
|
---|
[1] | 184 | }
|
---|
| 185 |
|
---|
[23973] | 186 | switch (fOpen & RTFILE_O_DENY_MASK)
|
---|
| 187 | {
|
---|
| 188 | case 0: /* temporarily */
|
---|
| 189 | AssertMsgFailed(("Missing RTFILE_O_DENY_*! (continuable assertion)\n"));
|
---|
| 190 | fOpen |= RTFILE_O_DENY_NONE;
|
---|
| 191 | break;
|
---|
| 192 | case RTFILE_O_DENY_NONE:
|
---|
| 193 | case RTFILE_O_DENY_READ:
|
---|
| 194 | case RTFILE_O_DENY_WRITE:
|
---|
| 195 | case RTFILE_O_DENY_WRITE | RTFILE_O_DENY_READ:
|
---|
| 196 | case RTFILE_O_DENY_NOT_DELETE:
|
---|
| 197 | case RTFILE_O_DENY_NOT_DELETE | RTFILE_O_DENY_READ:
|
---|
| 198 | case RTFILE_O_DENY_NOT_DELETE | RTFILE_O_DENY_WRITE:
|
---|
| 199 | case RTFILE_O_DENY_NOT_DELETE | RTFILE_O_DENY_WRITE | RTFILE_O_DENY_READ:
|
---|
| 200 | break;
|
---|
| 201 | default:
|
---|
[37596] | 202 | AssertMsgFailed(("Invalid deny value: fOpen=%#llx\n", fOpen));
|
---|
[23973] | 203 | return VERR_INVALID_PARAMETER;
|
---|
| 204 | }
|
---|
| 205 |
|
---|
[1] | 206 | /* done */
|
---|
| 207 | *pfOpen = fOpen;
|
---|
| 208 | return VINF_SUCCESS;
|
---|
| 209 | }
|
---|
| 210 |
|
---|
| 211 |
|
---|
| 212 | RTR3DECL(uint64_t) RTFileTell(RTFILE File)
|
---|
| 213 | {
|
---|
| 214 | /*
|
---|
| 215 | * Call the seek api to query the stuff.
|
---|
| 216 | */
|
---|
| 217 | uint64_t off = 0;
|
---|
| 218 | int rc = RTFileSeek(File, 0, RTFILE_SEEK_CURRENT, &off);
|
---|
| 219 | if (RT_SUCCESS(rc))
|
---|
| 220 | return off;
|
---|
| 221 | AssertMsgFailed(("RTFileSeek(%d) -> %d\n", File, rc));
|
---|
| 222 | return ~0ULL;
|
---|
| 223 | }
|
---|
| 224 |
|
---|
| 225 |
|
---|
[6429] | 226 | RTR3DECL(RTFOFF) RTFileGetMaxSize(RTFILE File)
|
---|
[6421] | 227 | {
|
---|
[6429] | 228 | RTFOFF cbMax;
|
---|
[80585] | 229 | int rc = RTFileQueryMaxSizeEx(File, &cbMax);
|
---|
[6429] | 230 | return RT_SUCCESS(rc) ? cbMax : -1;
|
---|
| 231 | }
|
---|
| 232 |
|
---|
| 233 |
|
---|
[1] | 234 | RTDECL(int) RTFileCopyByHandles(RTFILE FileSrc, RTFILE FileDst)
|
---|
| 235 | {
|
---|
| 236 | return RTFileCopyByHandlesEx(FileSrc, FileDst, NULL, NULL);
|
---|
| 237 | }
|
---|
| 238 |
|
---|
| 239 |
|
---|
[49033] | 240 | RTDECL(int) RTFileCompare(const char *pszFile1, const char *pszFile2)
|
---|
| 241 | {
|
---|
| 242 | return RTFileCompareEx(pszFile1, pszFile2, 0 /*fFlags*/, NULL, NULL);
|
---|
| 243 | }
|
---|
| 244 |
|
---|
| 245 |
|
---|
| 246 | RTDECL(int) RTFileCompareByHandles(RTFILE hFile1, RTFILE hFile2)
|
---|
| 247 | {
|
---|
| 248 | return RTFileCompareByHandlesEx(hFile1, hFile2, 0 /*fFlags*/, NULL, NULL);
|
---|
| 249 | }
|
---|
| 250 |
|
---|
| 251 |
|
---|
| 252 | RTDECL(int) RTFileCompareEx(const char *pszFile1, const char *pszFile2, uint32_t fFlags, PFNRTPROGRESS pfnProgress, void *pvUser)
|
---|
| 253 | {
|
---|
| 254 | /*
|
---|
| 255 | * Validate input.
|
---|
| 256 | */
|
---|
| 257 | AssertPtrReturn(pszFile1, VERR_INVALID_POINTER);
|
---|
| 258 | AssertReturn(*pszFile1, VERR_INVALID_PARAMETER);
|
---|
| 259 | AssertPtrReturn(pszFile2, VERR_INVALID_POINTER);
|
---|
| 260 | AssertReturn(*pszFile2, VERR_INVALID_PARAMETER);
|
---|
[90803] | 261 | AssertPtrNullReturn(pfnProgress, VERR_INVALID_POINTER);
|
---|
[49033] | 262 | AssertMsgReturn(!(fFlags & ~RTFILECOMP_FLAGS_MASK), ("%#x\n", fFlags), VERR_INVALID_PARAMETER);
|
---|
| 263 |
|
---|
| 264 | /*
|
---|
| 265 | * Open the files.
|
---|
| 266 | */
|
---|
| 267 | RTFILE hFile1;
|
---|
| 268 | int rc = RTFileOpen(&hFile1, pszFile1,
|
---|
| 269 | RTFILE_O_READ | RTFILE_O_OPEN
|
---|
| 270 | | (fFlags & RTFILECOMP_FLAGS_NO_DENY_WRITE_FILE1 ? RTFILE_O_DENY_NONE : RTFILE_O_DENY_WRITE));
|
---|
| 271 | if (RT_SUCCESS(rc))
|
---|
| 272 | {
|
---|
| 273 | RTFILE hFile2;
|
---|
| 274 | rc = RTFileOpen(&hFile2, pszFile2,
|
---|
[49036] | 275 | RTFILE_O_READ | RTFILE_O_OPEN
|
---|
[49033] | 276 | | (fFlags & RTFILECOMP_FLAGS_NO_DENY_WRITE_FILE2 ? RTFILE_O_DENY_NONE : RTFILE_O_DENY_WRITE));
|
---|
| 277 | if (RT_SUCCESS(rc))
|
---|
| 278 | {
|
---|
| 279 | /*
|
---|
| 280 | * Call the ByHandles version and let it do the job.
|
---|
| 281 | */
|
---|
| 282 | rc = RTFileCompareByHandlesEx(hFile1, hFile2, fFlags, pfnProgress, pvUser);
|
---|
| 283 |
|
---|
| 284 | /* Clean up */
|
---|
| 285 | int rc2 = RTFileClose(hFile2);
|
---|
| 286 | AssertRC(rc2);
|
---|
| 287 | if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
|
---|
| 288 | rc = rc2;
|
---|
| 289 | }
|
---|
| 290 |
|
---|
[49037] | 291 | int rc2 = RTFileClose(hFile1);
|
---|
[49033] | 292 | AssertRC(rc2);
|
---|
| 293 | if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
|
---|
| 294 | rc = rc2;
|
---|
| 295 | }
|
---|
| 296 | return rc;
|
---|
| 297 | }
|
---|
| 298 |
|
---|
| 299 |
|
---|
| 300 | RTDECL(int) RTFileCompareByHandlesEx(RTFILE hFile1, RTFILE hFile2, uint32_t fFlags, PFNRTPROGRESS pfnProgress, void *pvUser)
|
---|
| 301 | {
|
---|
| 302 | /*
|
---|
| 303 | * Validate input.
|
---|
| 304 | */
|
---|
| 305 | AssertReturn(RTFileIsValid(hFile1), VERR_INVALID_HANDLE);
|
---|
| 306 | AssertReturn(RTFileIsValid(hFile1), VERR_INVALID_HANDLE);
|
---|
[90803] | 307 | AssertPtrNullReturn(pfnProgress, VERR_INVALID_POINTER);
|
---|
[49033] | 308 | AssertMsgReturn(!(fFlags & ~RTFILECOMP_FLAGS_MASK), ("%#x\n", fFlags), VERR_INVALID_PARAMETER);
|
---|
| 309 |
|
---|
| 310 | /*
|
---|
| 311 | * Compare the file sizes first.
|
---|
| 312 | */
|
---|
| 313 | uint64_t cbFile1;
|
---|
[80585] | 314 | int rc = RTFileQuerySize(hFile1, &cbFile1);
|
---|
[49033] | 315 | if (RT_FAILURE(rc))
|
---|
| 316 | return rc;
|
---|
| 317 |
|
---|
| 318 | uint64_t cbFile2;
|
---|
[80585] | 319 | rc = RTFileQuerySize(hFile1, &cbFile2);
|
---|
[49033] | 320 | if (RT_FAILURE(rc))
|
---|
| 321 | return rc;
|
---|
| 322 |
|
---|
| 323 | if (cbFile1 != cbFile2)
|
---|
| 324 | return VERR_NOT_EQUAL;
|
---|
| 325 |
|
---|
| 326 |
|
---|
| 327 | /*
|
---|
| 328 | * Allocate buffer.
|
---|
| 329 | */
|
---|
| 330 | size_t cbBuf;
|
---|
| 331 | uint8_t *pbBuf1Free = NULL;
|
---|
| 332 | uint8_t *pbBuf1;
|
---|
| 333 | uint8_t *pbBuf2Free = NULL;
|
---|
| 334 | uint8_t *pbBuf2;
|
---|
| 335 | if (cbFile1 < _512K)
|
---|
| 336 | {
|
---|
| 337 | cbBuf = 8*_1K;
|
---|
| 338 | pbBuf1 = (uint8_t *)alloca(cbBuf);
|
---|
| 339 | pbBuf2 = (uint8_t *)alloca(cbBuf);
|
---|
| 340 | }
|
---|
| 341 | else
|
---|
| 342 | {
|
---|
| 343 | cbBuf = _128K;
|
---|
| 344 | pbBuf1 = pbBuf1Free = (uint8_t *)RTMemTmpAlloc(cbBuf);
|
---|
| 345 | pbBuf2 = pbBuf2Free = (uint8_t *)RTMemTmpAlloc(cbBuf);
|
---|
| 346 | }
|
---|
| 347 | if (pbBuf1 && pbBuf2)
|
---|
| 348 | {
|
---|
| 349 | /*
|
---|
| 350 | * Seek to the start of each file
|
---|
| 351 | * and set the size of the destination file.
|
---|
| 352 | */
|
---|
| 353 | rc = RTFileSeek(hFile1, 0, RTFILE_SEEK_BEGIN, NULL);
|
---|
| 354 | if (RT_SUCCESS(rc))
|
---|
| 355 | {
|
---|
| 356 | rc = RTFileSeek(hFile2, 0, RTFILE_SEEK_BEGIN, NULL);
|
---|
| 357 | if (RT_SUCCESS(rc) && pfnProgress)
|
---|
| 358 | rc = pfnProgress(0, pvUser);
|
---|
| 359 | if (RT_SUCCESS(rc))
|
---|
| 360 | {
|
---|
| 361 | /*
|
---|
| 362 | * Compare loop.
|
---|
| 363 | */
|
---|
| 364 | unsigned uPercentage = 0;
|
---|
| 365 | RTFOFF off = 0;
|
---|
| 366 | RTFOFF cbPercent = cbFile1 / 100;
|
---|
| 367 | RTFOFF offNextPercent = cbPercent;
|
---|
| 368 | while (off < (RTFOFF)cbFile1)
|
---|
| 369 | {
|
---|
| 370 | /* read the blocks */
|
---|
| 371 | RTFOFF cbLeft = cbFile1 - off;
|
---|
| 372 | size_t cbBlock = cbLeft >= (RTFOFF)cbBuf ? cbBuf : (size_t)cbLeft;
|
---|
| 373 | rc = RTFileRead(hFile1, pbBuf1, cbBlock, NULL);
|
---|
| 374 | if (RT_FAILURE(rc))
|
---|
| 375 | break;
|
---|
| 376 | rc = RTFileRead(hFile2, pbBuf2, cbBlock, NULL);
|
---|
| 377 | if (RT_FAILURE(rc))
|
---|
| 378 | break;
|
---|
| 379 |
|
---|
| 380 | /* compare */
|
---|
| 381 | if (memcmp(pbBuf1, pbBuf2, cbBlock))
|
---|
| 382 | {
|
---|
| 383 | rc = VERR_NOT_EQUAL;
|
---|
| 384 | break;
|
---|
| 385 | }
|
---|
| 386 |
|
---|
| 387 | /* advance */
|
---|
| 388 | off += cbBlock;
|
---|
| 389 | if (pfnProgress && offNextPercent < off)
|
---|
| 390 | {
|
---|
| 391 | while (offNextPercent < off)
|
---|
| 392 | {
|
---|
| 393 | uPercentage++;
|
---|
| 394 | offNextPercent += cbPercent;
|
---|
| 395 | }
|
---|
| 396 | rc = pfnProgress(uPercentage, pvUser);
|
---|
| 397 | if (RT_FAILURE(rc))
|
---|
| 398 | break;
|
---|
| 399 | }
|
---|
| 400 | }
|
---|
| 401 |
|
---|
| 402 | #if 0
|
---|
| 403 | /*
|
---|
| 404 | * Compare OS specific data (EAs and stuff).
|
---|
| 405 | */
|
---|
| 406 | if (RT_SUCCESS(rc))
|
---|
| 407 | rc = rtFileCompareOSStuff(hFile1, hFile2);
|
---|
| 408 | #endif
|
---|
| 409 |
|
---|
| 410 | /* 100% */
|
---|
| 411 | if (pfnProgress && uPercentage < 100 && RT_SUCCESS(rc))
|
---|
| 412 | rc = pfnProgress(100, pvUser);
|
---|
| 413 | }
|
---|
| 414 | }
|
---|
| 415 | }
|
---|
| 416 | else
|
---|
| 417 | rc = VERR_NO_MEMORY;
|
---|
| 418 | RTMemTmpFree(pbBuf2Free);
|
---|
| 419 | RTMemTmpFree(pbBuf1Free);
|
---|
| 420 |
|
---|
| 421 | return rc;
|
---|
| 422 | }
|
---|
| 423 |
|
---|