VirtualBox

source: vbox/trunk/src/VBox/Runtime/r3/fileio.cpp@ 76553

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

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 23.2 KB
Line 
1/* $Id: fileio.cpp 76553 2019-01-01 01:45:53Z vboxsync $ */
2/** @file
3 * IPRT - File I/O.
4 */
5
6/*
7 * Copyright (C) 2006-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 "internal/iprt.h"
32#include <iprt/file.h>
33
34#include <iprt/mem.h>
35#include <iprt/assert.h>
36#include <iprt/alloca.h>
37#include <iprt/string.h>
38#include <iprt/err.h>
39#include "internal/file.h"
40
41
42/*********************************************************************************************************************************
43* Global Variables *
44*********************************************************************************************************************************/
45/** Set of forced set open flags for files opened read-only. */
46static unsigned g_fOpenReadSet = 0;
47
48/** Set of forced cleared open flags for files opened read-only. */
49static unsigned g_fOpenReadMask = 0;
50
51/** Set of forced set open flags for files opened write-only. */
52static unsigned g_fOpenWriteSet = 0;
53
54/** Set of forced cleared open flags for files opened write-only. */
55static unsigned g_fOpenWriteMask = 0;
56
57/** Set of forced set open flags for files opened read-write. */
58static unsigned g_fOpenReadWriteSet = 0;
59
60/** Set of forced cleared open flags for files opened read-write. */
61static unsigned g_fOpenReadWriteMask = 0;
62
63
64/**
65 * Force the use of open flags for all files opened after the setting is
66 * changed. The caller is responsible for not causing races with RTFileOpen().
67 *
68 * @returns iprt status code.
69 * @param fOpenForAccess Access mode to which the set/mask settings apply.
70 * @param fSet Open flags to be forced set.
71 * @param fMask Open flags to be masked out.
72 */
73RTR3DECL(int) RTFileSetForceFlags(unsigned fOpenForAccess, unsigned fSet, unsigned fMask)
74{
75 /*
76 * For now allow only RTFILE_O_WRITE_THROUGH. The other flags either
77 * make no sense in this context or are not useful to apply to all files.
78 */
79 if ((fSet | fMask) & ~RTFILE_O_WRITE_THROUGH)
80 return VERR_INVALID_PARAMETER;
81 switch (fOpenForAccess)
82 {
83 case RTFILE_O_READ:
84 g_fOpenReadSet = fSet;
85 g_fOpenReadMask = fMask;
86 break;
87 case RTFILE_O_WRITE:
88 g_fOpenWriteSet = fSet;
89 g_fOpenWriteMask = fMask;
90 break;
91 case RTFILE_O_READWRITE:
92 g_fOpenReadWriteSet = fSet;
93 g_fOpenReadWriteMask = fMask;
94 break;
95 default:
96 AssertMsgFailed(("Invalid access mode %d\n", fOpenForAccess));
97 return VERR_INVALID_PARAMETER;
98 }
99 return VINF_SUCCESS;
100}
101
102
103/**
104 * Adjusts and validates the flags.
105 *
106 * The adjustments are made according to the wishes specified using the RTFileSetForceFlags API.
107 *
108 * @returns IPRT status code.
109 * @param pfOpen Pointer to the user specified flags on input.
110 * Updated on successful return.
111 * @internal
112 */
113int rtFileRecalcAndValidateFlags(uint64_t *pfOpen)
114{
115 /*
116 * Recalc.
117 */
118 uint32_t fOpen = *pfOpen;
119 switch (fOpen & RTFILE_O_ACCESS_MASK)
120 {
121 case RTFILE_O_READ:
122 fOpen |= g_fOpenReadSet;
123 fOpen &= ~g_fOpenReadMask;
124 break;
125 case RTFILE_O_WRITE:
126 fOpen |= g_fOpenWriteSet;
127 fOpen &= ~g_fOpenWriteMask;
128 break;
129 case RTFILE_O_READWRITE:
130 fOpen |= g_fOpenReadWriteSet;
131 fOpen &= ~g_fOpenReadWriteMask;
132 break;
133#ifdef RT_OS_WINDOWS
134 case RTFILE_O_ATTR_ONLY:
135 if (fOpen & RTFILE_O_ACCESS_ATTR_MASK)
136 break;
137#endif
138 default:
139 AssertMsgFailed(("Invalid access mode value, fOpen=%#llx\n", fOpen));
140 return VERR_INVALID_PARAMETER;
141 }
142
143 /*
144 * Validate .
145 */
146#ifdef RT_OS_WINDOWS
147 AssertMsgReturn((fOpen & RTFILE_O_ACCESS_MASK) || (fOpen & RTFILE_O_ACCESS_ATTR_MASK),
148 ("Missing RTFILE_O_READ/WRITE/ACCESS_ATTR: fOpen=%#llx\n", fOpen), VERR_INVALID_PARAMETER);
149#else
150 AssertMsgReturn(fOpen & RTFILE_O_ACCESS_MASK, ("Missing RTFILE_O_READ/WRITE: fOpen=%#llx\n", fOpen), VERR_INVALID_PARAMETER);
151#endif
152#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
153 AssertMsgReturn(!(fOpen & (~(uint64_t)RTFILE_O_VALID_MASK | RTFILE_O_NON_BLOCK)), ("%#llx\n", fOpen), VERR_INVALID_PARAMETER);
154#else
155 AssertMsgReturn(!(fOpen & ~(uint64_t)RTFILE_O_VALID_MASK), ("%#llx\n", fOpen), VERR_INVALID_PARAMETER);
156#endif
157 AssertMsgReturn((fOpen & (RTFILE_O_TRUNCATE | RTFILE_O_WRITE)) != RTFILE_O_TRUNCATE, ("%#llx\n", fOpen), VERR_INVALID_PARAMETER);
158
159 switch (fOpen & RTFILE_O_ACTION_MASK)
160 {
161 case 0: /* temporarily */
162 AssertMsgFailed(("Missing RTFILE_O_OPEN/CREATE*! (continuable assertion)\n"));
163 fOpen |= RTFILE_O_OPEN;
164 break;
165 case RTFILE_O_OPEN:
166 AssertMsgReturn(!(RTFILE_O_NOT_CONTENT_INDEXED & fOpen), ("%#llx\n", fOpen), VERR_INVALID_PARAMETER);
167 case RTFILE_O_OPEN_CREATE:
168 case RTFILE_O_CREATE:
169 case RTFILE_O_CREATE_REPLACE:
170 break;
171 default:
172 AssertMsgFailed(("Invalid action value: fOpen=%#llx\n", fOpen));
173 return VERR_INVALID_PARAMETER;
174 }
175
176 switch (fOpen & RTFILE_O_DENY_MASK)
177 {
178 case 0: /* temporarily */
179 AssertMsgFailed(("Missing RTFILE_O_DENY_*! (continuable assertion)\n"));
180 fOpen |= RTFILE_O_DENY_NONE;
181 break;
182 case RTFILE_O_DENY_NONE:
183 case RTFILE_O_DENY_READ:
184 case RTFILE_O_DENY_WRITE:
185 case RTFILE_O_DENY_WRITE | RTFILE_O_DENY_READ:
186 case RTFILE_O_DENY_NOT_DELETE:
187 case RTFILE_O_DENY_NOT_DELETE | RTFILE_O_DENY_READ:
188 case RTFILE_O_DENY_NOT_DELETE | RTFILE_O_DENY_WRITE:
189 case RTFILE_O_DENY_NOT_DELETE | RTFILE_O_DENY_WRITE | RTFILE_O_DENY_READ:
190 break;
191 default:
192 AssertMsgFailed(("Invalid deny value: fOpen=%#llx\n", fOpen));
193 return VERR_INVALID_PARAMETER;
194 }
195
196 /* done */
197 *pfOpen = fOpen;
198 return VINF_SUCCESS;
199}
200
201
202
203/**
204 * Read bytes from a file at a given offset.
205 * This function may modify the file position.
206 *
207 * @returns iprt status code.
208 * @param File Handle to the file.
209 * @param off Where to read.
210 * @param pvBuf Where to put the bytes we read.
211 * @param cbToRead How much to read.
212 * @param *pcbRead How much we actually read.
213 * If NULL an error will be returned for a partial read.
214 */
215RTR3DECL(int) RTFileReadAt(RTFILE File, RTFOFF off, void *pvBuf, size_t cbToRead, size_t *pcbRead)
216{
217 int rc = RTFileSeek(File, off, RTFILE_SEEK_BEGIN, NULL);
218 if (RT_SUCCESS(rc))
219 rc = RTFileRead(File, pvBuf, cbToRead, pcbRead);
220 return rc;
221}
222
223
224/**
225 * Read bytes from a file at a given offset into a S/G buffer.
226 * This function may modify the file position.
227 *
228 * @returns iprt status code.
229 * @param hFile Handle to the file.
230 * @param off Where to read.
231 * @param pSgBuf Pointer to the S/G buffer to read into.
232 * @param cbToRead How much to read.
233 * @param pcbRead How much we actually read.
234 * If NULL an error will be returned for a partial read.
235 */
236RTR3DECL(int) RTFileSgReadAt(RTFILE hFile, RTFOFF off, PRTSGBUF pSgBuf, size_t cbToRead, size_t *pcbRead)
237{
238 int rc = VINF_SUCCESS;
239 size_t cbRead = 0;
240
241 while (cbToRead)
242 {
243 size_t cbThisRead = 0;
244 size_t cbBuf = cbToRead;
245 void *pvBuf = RTSgBufGetNextSegment(pSgBuf, &cbBuf);
246
247 rc = RTFileReadAt(hFile, off, pvBuf, cbBuf, pcbRead ? &cbThisRead : NULL);
248 if (RT_SUCCESS(rc))
249 cbRead += cbThisRead;
250
251 if ( RT_FAILURE(rc)
252 || ( cbThisRead < cbBuf
253 && pcbRead))
254 break;
255
256 cbToRead -= cbBuf;
257 off += cbBuf;
258 }
259
260 if (pcbRead)
261 *pcbRead = cbRead;
262
263 return rc;
264}
265
266
267/**
268 * Write bytes to a file at a given offset.
269 * This function may modify the file position.
270 *
271 * @returns iprt status code.
272 * @param File Handle to the file.
273 * @param off Where to write.
274 * @param pvBuf What to write.
275 * @param cbToWrite How much to write.
276 * @param *pcbWritten How much we actually wrote.
277 * If NULL an error will be returned for a partial write.
278 */
279RTR3DECL(int) RTFileWriteAt(RTFILE File, RTFOFF off, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
280{
281 int rc = RTFileSeek(File, off, RTFILE_SEEK_BEGIN, NULL);
282 if (RT_SUCCESS(rc))
283 rc = RTFileWrite(File, pvBuf, cbToWrite, pcbWritten);
284 return rc;
285}
286
287
288/**
289 * Write bytes from a S/G buffer to a file at a given offset.
290 * This function may modify the file position.
291 *
292 * @returns iprt status code.
293 * @param hFile Handle to the file.
294 * @param off Where to write.
295 * @param pSgBuf What to write.
296 * @param cbToWrite How much to write.
297 * @param pcbWritten How much we actually wrote.
298 * If NULL an error will be returned for a partial write.
299 */
300RTR3DECL(int) RTFileSgWriteAt(RTFILE hFile, RTFOFF off, PRTSGBUF pSgBuf, size_t cbToWrite, size_t *pcbWritten)
301{
302 int rc = VINF_SUCCESS;
303 size_t cbWritten = 0;
304
305 while (cbToWrite)
306 {
307 size_t cbThisWritten = 0;
308 size_t cbBuf = cbToWrite;
309 void *pvBuf = RTSgBufGetNextSegment(pSgBuf, &cbBuf);
310
311 rc = RTFileWriteAt(hFile, off, pvBuf, cbBuf, pcbWritten ? &cbThisWritten : NULL);
312 if (RT_SUCCESS(rc))
313 cbWritten += cbThisWritten;
314
315 if ( RT_FAILURE(rc)
316 || ( cbThisWritten < cbBuf
317 && pcbWritten))
318 break;
319
320 cbToWrite -= cbBuf;
321 off += cbBuf;
322 }
323
324 if (pcbWritten)
325 *pcbWritten = cbWritten;
326
327 return rc;
328}
329
330
331/**
332 * Gets the current file position.
333 *
334 * @returns File offset.
335 * @returns ~0UUL on failure.
336 * @param File File handle.
337 */
338RTR3DECL(uint64_t) RTFileTell(RTFILE File)
339{
340 /*
341 * Call the seek api to query the stuff.
342 */
343 uint64_t off = 0;
344 int rc = RTFileSeek(File, 0, RTFILE_SEEK_CURRENT, &off);
345 if (RT_SUCCESS(rc))
346 return off;
347 AssertMsgFailed(("RTFileSeek(%d) -> %d\n", File, rc));
348 return ~0ULL;
349}
350
351
352/**
353 * Determine the maximum file size.
354 *
355 * @returns The max size of the file.
356 * -1 on failure, the file position is undefined.
357 * @param File Handle to the file.
358 * @see RTFileGetMaxSizeEx.
359 */
360RTR3DECL(RTFOFF) RTFileGetMaxSize(RTFILE File)
361{
362 RTFOFF cbMax;
363 int rc = RTFileGetMaxSizeEx(File, &cbMax);
364 return RT_SUCCESS(rc) ? cbMax : -1;
365}
366
367
368RTDECL(int) RTFileCopyByHandles(RTFILE FileSrc, RTFILE FileDst)
369{
370 return RTFileCopyByHandlesEx(FileSrc, FileDst, NULL, NULL);
371}
372
373
374RTDECL(int) RTFileCopyEx(const char *pszSrc, const char *pszDst, uint32_t fFlags, PFNRTPROGRESS pfnProgress, void *pvUser)
375{
376 /*
377 * Validate input.
378 */
379 AssertMsgReturn(VALID_PTR(pszSrc), ("pszSrc=%p\n", pszSrc), VERR_INVALID_PARAMETER);
380 AssertMsgReturn(*pszSrc, ("pszSrc=%p\n", pszSrc), VERR_INVALID_PARAMETER);
381 AssertMsgReturn(VALID_PTR(pszDst), ("pszDst=%p\n", pszDst), VERR_INVALID_PARAMETER);
382 AssertMsgReturn(*pszDst, ("pszDst=%p\n", pszDst), VERR_INVALID_PARAMETER);
383 AssertMsgReturn(!pfnProgress || VALID_PTR(pfnProgress), ("pfnProgress=%p\n", pfnProgress), VERR_INVALID_PARAMETER);
384 AssertMsgReturn(!(fFlags & ~RTFILECOPY_FLAGS_MASK), ("%#x\n", fFlags), VERR_INVALID_PARAMETER);
385
386 /*
387 * Open the files.
388 */
389 RTFILE FileSrc;
390 int rc = RTFileOpen(&FileSrc, pszSrc,
391 RTFILE_O_READ | RTFILE_O_OPEN
392 | (fFlags & RTFILECOPY_FLAGS_NO_SRC_DENY_WRITE ? RTFILE_O_DENY_NONE : RTFILE_O_DENY_WRITE));
393 if (RT_SUCCESS(rc))
394 {
395 RTFILE FileDst;
396 rc = RTFileOpen(&FileDst, pszDst,
397 RTFILE_O_WRITE | RTFILE_O_CREATE
398 | (fFlags & RTFILECOPY_FLAGS_NO_DST_DENY_WRITE ? RTFILE_O_DENY_NONE : RTFILE_O_DENY_WRITE));
399 if (RT_SUCCESS(rc))
400 {
401 /*
402 * Call the ByHandles version and let it do the job.
403 */
404 rc = RTFileCopyByHandlesEx(FileSrc, FileDst, pfnProgress, pvUser);
405
406 /*
407 * Close the files regardless of the result.
408 * Don't bother cleaning up or anything like that.
409 */
410 int rc2 = RTFileClose(FileDst);
411 AssertRC(rc2);
412 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
413 rc = rc2;
414 }
415
416 int rc2 = RTFileClose(FileSrc);
417 AssertRC(rc2);
418 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
419 rc = rc2;
420 }
421 return rc;
422}
423
424
425RTDECL(int) RTFileCopyByHandlesEx(RTFILE FileSrc, RTFILE FileDst, PFNRTPROGRESS pfnProgress, void *pvUser)
426{
427 /*
428 * Validate input.
429 */
430 AssertMsgReturn(RTFileIsValid(FileSrc), ("FileSrc=%RTfile\n", FileSrc), VERR_INVALID_PARAMETER);
431 AssertMsgReturn(RTFileIsValid(FileDst), ("FileDst=%RTfile\n", FileDst), VERR_INVALID_PARAMETER);
432 AssertMsgReturn(!pfnProgress || VALID_PTR(pfnProgress), ("pfnProgress=%p\n", pfnProgress), VERR_INVALID_PARAMETER);
433
434 /*
435 * Save file offset.
436 */
437 RTFOFF offSrcSaved;
438 int rc = RTFileSeek(FileSrc, 0, RTFILE_SEEK_CURRENT, (uint64_t *)&offSrcSaved);
439 if (RT_FAILURE(rc))
440 return rc;
441
442 /*
443 * Get the file size.
444 */
445 RTFOFF cbSrc;
446 rc = RTFileSeek(FileSrc, 0, RTFILE_SEEK_END, (uint64_t *)&cbSrc);
447 if (RT_FAILURE(rc))
448 return rc;
449
450 /*
451 * Allocate buffer.
452 */
453 size_t cbBuf;
454 uint8_t *pbBufFree = NULL;
455 uint8_t *pbBuf;
456 if (cbSrc < _512K)
457 {
458 cbBuf = 8*_1K;
459 pbBuf = (uint8_t *)alloca(cbBuf);
460 }
461 else
462 {
463 cbBuf = _128K;
464 pbBuf = pbBufFree = (uint8_t *)RTMemTmpAlloc(cbBuf);
465 }
466 if (pbBuf)
467 {
468 /*
469 * Seek to the start of each file
470 * and set the size of the destination file.
471 */
472 rc = RTFileSeek(FileSrc, 0, RTFILE_SEEK_BEGIN, NULL);
473 if (RT_SUCCESS(rc))
474 {
475 rc = RTFileSeek(FileDst, 0, RTFILE_SEEK_BEGIN, NULL);
476 if (RT_SUCCESS(rc))
477 rc = RTFileSetSize(FileDst, cbSrc);
478 if (RT_SUCCESS(rc) && pfnProgress)
479 rc = pfnProgress(0, pvUser);
480 if (RT_SUCCESS(rc))
481 {
482 /*
483 * Copy loop.
484 */
485 unsigned uPercentage = 0;
486 RTFOFF off = 0;
487 RTFOFF cbPercent = cbSrc / 100;
488 RTFOFF offNextPercent = cbPercent;
489 while (off < cbSrc)
490 {
491 /* copy block */
492 RTFOFF cbLeft = cbSrc - off;
493 size_t cbBlock = cbLeft >= (RTFOFF)cbBuf ? cbBuf : (size_t)cbLeft;
494 rc = RTFileRead(FileSrc, pbBuf, cbBlock, NULL);
495 if (RT_FAILURE(rc))
496 break;
497 rc = RTFileWrite(FileDst, pbBuf, cbBlock, NULL);
498 if (RT_FAILURE(rc))
499 break;
500
501 /* advance */
502 off += cbBlock;
503 if (pfnProgress && offNextPercent < off && uPercentage < 100)
504 {
505 do
506 {
507 uPercentage++;
508 offNextPercent += cbPercent;
509 } while (offNextPercent < off && uPercentage < 100);
510 rc = pfnProgress(uPercentage, pvUser);
511 if (RT_FAILURE(rc))
512 break;
513 }
514 }
515
516#if 0
517 /*
518 * Copy OS specific data (EAs and stuff).
519 */
520 rtFileCopyOSStuff(FileSrc, FileDst);
521#endif
522
523 /* 100% */
524 if (pfnProgress && uPercentage < 100 && RT_SUCCESS(rc))
525 rc = pfnProgress(100, pvUser);
526 }
527 }
528 RTMemTmpFree(pbBufFree);
529 }
530 else
531 rc = VERR_NO_MEMORY;
532
533 /*
534 * Restore source position.
535 */
536 RTFileSeek(FileSrc, offSrcSaved, RTFILE_SEEK_BEGIN, NULL);
537
538 return rc;
539}
540
541
542RTDECL(int) RTFileCompare(const char *pszFile1, const char *pszFile2)
543{
544 return RTFileCompareEx(pszFile1, pszFile2, 0 /*fFlags*/, NULL, NULL);
545}
546
547
548RTDECL(int) RTFileCompareByHandles(RTFILE hFile1, RTFILE hFile2)
549{
550 return RTFileCompareByHandlesEx(hFile1, hFile2, 0 /*fFlags*/, NULL, NULL);
551}
552
553
554RTDECL(int) RTFileCompareEx(const char *pszFile1, const char *pszFile2, uint32_t fFlags, PFNRTPROGRESS pfnProgress, void *pvUser)
555{
556 /*
557 * Validate input.
558 */
559 AssertPtrReturn(pszFile1, VERR_INVALID_POINTER);
560 AssertReturn(*pszFile1, VERR_INVALID_PARAMETER);
561 AssertPtrReturn(pszFile2, VERR_INVALID_POINTER);
562 AssertReturn(*pszFile2, VERR_INVALID_PARAMETER);
563 AssertMsgReturn(!pfnProgress || VALID_PTR(pfnProgress), ("pfnProgress=%p\n", pfnProgress), VERR_INVALID_PARAMETER);
564 AssertMsgReturn(!(fFlags & ~RTFILECOMP_FLAGS_MASK), ("%#x\n", fFlags), VERR_INVALID_PARAMETER);
565
566 /*
567 * Open the files.
568 */
569 RTFILE hFile1;
570 int rc = RTFileOpen(&hFile1, pszFile1,
571 RTFILE_O_READ | RTFILE_O_OPEN
572 | (fFlags & RTFILECOMP_FLAGS_NO_DENY_WRITE_FILE1 ? RTFILE_O_DENY_NONE : RTFILE_O_DENY_WRITE));
573 if (RT_SUCCESS(rc))
574 {
575 RTFILE hFile2;
576 rc = RTFileOpen(&hFile2, pszFile2,
577 RTFILE_O_READ | RTFILE_O_OPEN
578 | (fFlags & RTFILECOMP_FLAGS_NO_DENY_WRITE_FILE2 ? RTFILE_O_DENY_NONE : RTFILE_O_DENY_WRITE));
579 if (RT_SUCCESS(rc))
580 {
581 /*
582 * Call the ByHandles version and let it do the job.
583 */
584 rc = RTFileCompareByHandlesEx(hFile1, hFile2, fFlags, pfnProgress, pvUser);
585
586 /* Clean up */
587 int rc2 = RTFileClose(hFile2);
588 AssertRC(rc2);
589 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
590 rc = rc2;
591 }
592
593 int rc2 = RTFileClose(hFile1);
594 AssertRC(rc2);
595 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
596 rc = rc2;
597 }
598 return rc;
599}
600
601
602RTDECL(int) RTFileCompareByHandlesEx(RTFILE hFile1, RTFILE hFile2, uint32_t fFlags, PFNRTPROGRESS pfnProgress, void *pvUser)
603{
604 /*
605 * Validate input.
606 */
607 AssertReturn(RTFileIsValid(hFile1), VERR_INVALID_HANDLE);
608 AssertReturn(RTFileIsValid(hFile1), VERR_INVALID_HANDLE);
609 AssertMsgReturn(!pfnProgress || VALID_PTR(pfnProgress), ("pfnProgress=%p\n", pfnProgress), VERR_INVALID_PARAMETER);
610 AssertMsgReturn(!(fFlags & ~RTFILECOMP_FLAGS_MASK), ("%#x\n", fFlags), VERR_INVALID_PARAMETER);
611
612 /*
613 * Compare the file sizes first.
614 */
615 uint64_t cbFile1;
616 int rc = RTFileGetSize(hFile1, &cbFile1);
617 if (RT_FAILURE(rc))
618 return rc;
619
620 uint64_t cbFile2;
621 rc = RTFileGetSize(hFile1, &cbFile2);
622 if (RT_FAILURE(rc))
623 return rc;
624
625 if (cbFile1 != cbFile2)
626 return VERR_NOT_EQUAL;
627
628
629 /*
630 * Allocate buffer.
631 */
632 size_t cbBuf;
633 uint8_t *pbBuf1Free = NULL;
634 uint8_t *pbBuf1;
635 uint8_t *pbBuf2Free = NULL;
636 uint8_t *pbBuf2;
637 if (cbFile1 < _512K)
638 {
639 cbBuf = 8*_1K;
640 pbBuf1 = (uint8_t *)alloca(cbBuf);
641 pbBuf2 = (uint8_t *)alloca(cbBuf);
642 }
643 else
644 {
645 cbBuf = _128K;
646 pbBuf1 = pbBuf1Free = (uint8_t *)RTMemTmpAlloc(cbBuf);
647 pbBuf2 = pbBuf2Free = (uint8_t *)RTMemTmpAlloc(cbBuf);
648 }
649 if (pbBuf1 && pbBuf2)
650 {
651 /*
652 * Seek to the start of each file
653 * and set the size of the destination file.
654 */
655 rc = RTFileSeek(hFile1, 0, RTFILE_SEEK_BEGIN, NULL);
656 if (RT_SUCCESS(rc))
657 {
658 rc = RTFileSeek(hFile2, 0, RTFILE_SEEK_BEGIN, NULL);
659 if (RT_SUCCESS(rc) && pfnProgress)
660 rc = pfnProgress(0, pvUser);
661 if (RT_SUCCESS(rc))
662 {
663 /*
664 * Compare loop.
665 */
666 unsigned uPercentage = 0;
667 RTFOFF off = 0;
668 RTFOFF cbPercent = cbFile1 / 100;
669 RTFOFF offNextPercent = cbPercent;
670 while (off < (RTFOFF)cbFile1)
671 {
672 /* read the blocks */
673 RTFOFF cbLeft = cbFile1 - off;
674 size_t cbBlock = cbLeft >= (RTFOFF)cbBuf ? cbBuf : (size_t)cbLeft;
675 rc = RTFileRead(hFile1, pbBuf1, cbBlock, NULL);
676 if (RT_FAILURE(rc))
677 break;
678 rc = RTFileRead(hFile2, pbBuf2, cbBlock, NULL);
679 if (RT_FAILURE(rc))
680 break;
681
682 /* compare */
683 if (memcmp(pbBuf1, pbBuf2, cbBlock))
684 {
685 rc = VERR_NOT_EQUAL;
686 break;
687 }
688
689 /* advance */
690 off += cbBlock;
691 if (pfnProgress && offNextPercent < off)
692 {
693 while (offNextPercent < off)
694 {
695 uPercentage++;
696 offNextPercent += cbPercent;
697 }
698 rc = pfnProgress(uPercentage, pvUser);
699 if (RT_FAILURE(rc))
700 break;
701 }
702 }
703
704#if 0
705 /*
706 * Compare OS specific data (EAs and stuff).
707 */
708 if (RT_SUCCESS(rc))
709 rc = rtFileCompareOSStuff(hFile1, hFile2);
710#endif
711
712 /* 100% */
713 if (pfnProgress && uPercentage < 100 && RT_SUCCESS(rc))
714 rc = pfnProgress(100, pvUser);
715 }
716 }
717 }
718 else
719 rc = VERR_NO_MEMORY;
720 RTMemTmpFree(pbBuf2Free);
721 RTMemTmpFree(pbBuf1Free);
722
723 return rc;
724}
725
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use