VirtualBox

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

Last change on this file since 96507 was 96407, checked in by vboxsync, 2 years ago

scm copyright and license note update

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 14.1 KB
Line 
1/* $Id: fileio.cpp 96407 2022-08-22 17:43:14Z vboxsync $ */
2/** @file
3 * IPRT - File I/O.
4 */
5
6/*
7 * Copyright (C) 2006-2022 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 "internal/iprt.h"
42#include <iprt/file.h>
43
44#include <iprt/mem.h>
45#include <iprt/assert.h>
46#include <iprt/alloca.h>
47#include <iprt/string.h>
48#include <iprt/err.h>
49#include "internal/file.h"
50
51
52/*********************************************************************************************************************************
53* Global Variables *
54*********************************************************************************************************************************/
55/** Set of forced set open flags for files opened read-only. */
56static unsigned g_fOpenReadSet = 0;
57
58/** Set of forced cleared open flags for files opened read-only. */
59static unsigned g_fOpenReadMask = 0;
60
61/** Set of forced set open flags for files opened write-only. */
62static unsigned g_fOpenWriteSet = 0;
63
64/** Set of forced cleared open flags for files opened write-only. */
65static unsigned g_fOpenWriteMask = 0;
66
67/** Set of forced set open flags for files opened read-write. */
68static unsigned g_fOpenReadWriteSet = 0;
69
70/** Set of forced cleared open flags for files opened read-write. */
71static 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 */
83RTR3DECL(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 */
123int rtFileRecalcAndValidateFlags(uint64_t *pfOpen)
124{
125 /*
126 * Recalc.
127 */
128 uint32_t fOpen = *pfOpen;
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;
143#ifdef RT_OS_WINDOWS
144 case RTFILE_O_ATTR_ONLY:
145 if (fOpen & RTFILE_O_ACCESS_ATTR_MASK)
146 break;
147#endif
148 default:
149 AssertMsgFailed(("Invalid access mode value, fOpen=%#llx\n", fOpen));
150 return VERR_INVALID_PARAMETER;
151 }
152
153 /*
154 * Validate .
155 */
156#ifdef RT_OS_WINDOWS
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);
159#else
160 AssertMsgReturn(fOpen & RTFILE_O_ACCESS_MASK, ("Missing RTFILE_O_READ/WRITE: fOpen=%#llx\n", fOpen), VERR_INVALID_PARAMETER);
161#endif
162#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
163 AssertMsgReturn(!(fOpen & (~(uint64_t)RTFILE_O_VALID_MASK | RTFILE_O_NON_BLOCK)), ("%#llx\n", fOpen), VERR_INVALID_PARAMETER);
164#else
165 AssertMsgReturn(!(fOpen & ~(uint64_t)RTFILE_O_VALID_MASK), ("%#llx\n", fOpen), VERR_INVALID_PARAMETER);
166#endif
167 AssertMsgReturn((fOpen & (RTFILE_O_TRUNCATE | RTFILE_O_WRITE)) != RTFILE_O_TRUNCATE, ("%#llx\n", fOpen), VERR_INVALID_PARAMETER);
168
169 switch (fOpen & RTFILE_O_ACTION_MASK)
170 {
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:
176 AssertMsgReturn(!(RTFILE_O_NOT_CONTENT_INDEXED & fOpen), ("%#llx\n", fOpen), VERR_INVALID_PARAMETER);
177 case RTFILE_O_OPEN_CREATE:
178 case RTFILE_O_CREATE:
179 case RTFILE_O_CREATE_REPLACE:
180 break;
181 default:
182 AssertMsgFailed(("Invalid action value: fOpen=%#llx\n", fOpen));
183 return VERR_INVALID_PARAMETER;
184 }
185
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:
202 AssertMsgFailed(("Invalid deny value: fOpen=%#llx\n", fOpen));
203 return VERR_INVALID_PARAMETER;
204 }
205
206 /* done */
207 *pfOpen = fOpen;
208 return VINF_SUCCESS;
209}
210
211
212/**
213 * Gets the current file position.
214 *
215 * @returns File offset.
216 * @returns ~0UUL on failure.
217 * @param File File handle.
218 */
219RTR3DECL(uint64_t) RTFileTell(RTFILE File)
220{
221 /*
222 * Call the seek api to query the stuff.
223 */
224 uint64_t off = 0;
225 int rc = RTFileSeek(File, 0, RTFILE_SEEK_CURRENT, &off);
226 if (RT_SUCCESS(rc))
227 return off;
228 AssertMsgFailed(("RTFileSeek(%d) -> %d\n", File, rc));
229 return ~0ULL;
230}
231
232
233RTR3DECL(RTFOFF) RTFileGetMaxSize(RTFILE File)
234{
235 RTFOFF cbMax;
236 int rc = RTFileQueryMaxSizeEx(File, &cbMax);
237 return RT_SUCCESS(rc) ? cbMax : -1;
238}
239
240
241RTDECL(int) RTFileCopyByHandles(RTFILE FileSrc, RTFILE FileDst)
242{
243 return RTFileCopyByHandlesEx(FileSrc, FileDst, NULL, NULL);
244}
245
246
247RTDECL(int) RTFileCompare(const char *pszFile1, const char *pszFile2)
248{
249 return RTFileCompareEx(pszFile1, pszFile2, 0 /*fFlags*/, NULL, NULL);
250}
251
252
253RTDECL(int) RTFileCompareByHandles(RTFILE hFile1, RTFILE hFile2)
254{
255 return RTFileCompareByHandlesEx(hFile1, hFile2, 0 /*fFlags*/, NULL, NULL);
256}
257
258
259RTDECL(int) RTFileCompareEx(const char *pszFile1, const char *pszFile2, uint32_t fFlags, PFNRTPROGRESS pfnProgress, void *pvUser)
260{
261 /*
262 * Validate input.
263 */
264 AssertPtrReturn(pszFile1, VERR_INVALID_POINTER);
265 AssertReturn(*pszFile1, VERR_INVALID_PARAMETER);
266 AssertPtrReturn(pszFile2, VERR_INVALID_POINTER);
267 AssertReturn(*pszFile2, VERR_INVALID_PARAMETER);
268 AssertPtrNullReturn(pfnProgress, VERR_INVALID_POINTER);
269 AssertMsgReturn(!(fFlags & ~RTFILECOMP_FLAGS_MASK), ("%#x\n", fFlags), VERR_INVALID_PARAMETER);
270
271 /*
272 * Open the files.
273 */
274 RTFILE hFile1;
275 int rc = RTFileOpen(&hFile1, pszFile1,
276 RTFILE_O_READ | RTFILE_O_OPEN
277 | (fFlags & RTFILECOMP_FLAGS_NO_DENY_WRITE_FILE1 ? RTFILE_O_DENY_NONE : RTFILE_O_DENY_WRITE));
278 if (RT_SUCCESS(rc))
279 {
280 RTFILE hFile2;
281 rc = RTFileOpen(&hFile2, pszFile2,
282 RTFILE_O_READ | RTFILE_O_OPEN
283 | (fFlags & RTFILECOMP_FLAGS_NO_DENY_WRITE_FILE2 ? RTFILE_O_DENY_NONE : RTFILE_O_DENY_WRITE));
284 if (RT_SUCCESS(rc))
285 {
286 /*
287 * Call the ByHandles version and let it do the job.
288 */
289 rc = RTFileCompareByHandlesEx(hFile1, hFile2, fFlags, pfnProgress, pvUser);
290
291 /* Clean up */
292 int rc2 = RTFileClose(hFile2);
293 AssertRC(rc2);
294 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
295 rc = rc2;
296 }
297
298 int rc2 = RTFileClose(hFile1);
299 AssertRC(rc2);
300 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
301 rc = rc2;
302 }
303 return rc;
304}
305
306
307RTDECL(int) RTFileCompareByHandlesEx(RTFILE hFile1, RTFILE hFile2, uint32_t fFlags, PFNRTPROGRESS pfnProgress, void *pvUser)
308{
309 /*
310 * Validate input.
311 */
312 AssertReturn(RTFileIsValid(hFile1), VERR_INVALID_HANDLE);
313 AssertReturn(RTFileIsValid(hFile1), VERR_INVALID_HANDLE);
314 AssertPtrNullReturn(pfnProgress, VERR_INVALID_POINTER);
315 AssertMsgReturn(!(fFlags & ~RTFILECOMP_FLAGS_MASK), ("%#x\n", fFlags), VERR_INVALID_PARAMETER);
316
317 /*
318 * Compare the file sizes first.
319 */
320 uint64_t cbFile1;
321 int rc = RTFileQuerySize(hFile1, &cbFile1);
322 if (RT_FAILURE(rc))
323 return rc;
324
325 uint64_t cbFile2;
326 rc = RTFileQuerySize(hFile1, &cbFile2);
327 if (RT_FAILURE(rc))
328 return rc;
329
330 if (cbFile1 != cbFile2)
331 return VERR_NOT_EQUAL;
332
333
334 /*
335 * Allocate buffer.
336 */
337 size_t cbBuf;
338 uint8_t *pbBuf1Free = NULL;
339 uint8_t *pbBuf1;
340 uint8_t *pbBuf2Free = NULL;
341 uint8_t *pbBuf2;
342 if (cbFile1 < _512K)
343 {
344 cbBuf = 8*_1K;
345 pbBuf1 = (uint8_t *)alloca(cbBuf);
346 pbBuf2 = (uint8_t *)alloca(cbBuf);
347 }
348 else
349 {
350 cbBuf = _128K;
351 pbBuf1 = pbBuf1Free = (uint8_t *)RTMemTmpAlloc(cbBuf);
352 pbBuf2 = pbBuf2Free = (uint8_t *)RTMemTmpAlloc(cbBuf);
353 }
354 if (pbBuf1 && pbBuf2)
355 {
356 /*
357 * Seek to the start of each file
358 * and set the size of the destination file.
359 */
360 rc = RTFileSeek(hFile1, 0, RTFILE_SEEK_BEGIN, NULL);
361 if (RT_SUCCESS(rc))
362 {
363 rc = RTFileSeek(hFile2, 0, RTFILE_SEEK_BEGIN, NULL);
364 if (RT_SUCCESS(rc) && pfnProgress)
365 rc = pfnProgress(0, pvUser);
366 if (RT_SUCCESS(rc))
367 {
368 /*
369 * Compare loop.
370 */
371 unsigned uPercentage = 0;
372 RTFOFF off = 0;
373 RTFOFF cbPercent = cbFile1 / 100;
374 RTFOFF offNextPercent = cbPercent;
375 while (off < (RTFOFF)cbFile1)
376 {
377 /* read the blocks */
378 RTFOFF cbLeft = cbFile1 - off;
379 size_t cbBlock = cbLeft >= (RTFOFF)cbBuf ? cbBuf : (size_t)cbLeft;
380 rc = RTFileRead(hFile1, pbBuf1, cbBlock, NULL);
381 if (RT_FAILURE(rc))
382 break;
383 rc = RTFileRead(hFile2, pbBuf2, cbBlock, NULL);
384 if (RT_FAILURE(rc))
385 break;
386
387 /* compare */
388 if (memcmp(pbBuf1, pbBuf2, cbBlock))
389 {
390 rc = VERR_NOT_EQUAL;
391 break;
392 }
393
394 /* advance */
395 off += cbBlock;
396 if (pfnProgress && offNextPercent < off)
397 {
398 while (offNextPercent < off)
399 {
400 uPercentage++;
401 offNextPercent += cbPercent;
402 }
403 rc = pfnProgress(uPercentage, pvUser);
404 if (RT_FAILURE(rc))
405 break;
406 }
407 }
408
409#if 0
410 /*
411 * Compare OS specific data (EAs and stuff).
412 */
413 if (RT_SUCCESS(rc))
414 rc = rtFileCompareOSStuff(hFile1, hFile2);
415#endif
416
417 /* 100% */
418 if (pfnProgress && uPercentage < 100 && RT_SUCCESS(rc))
419 rc = pfnProgress(100, pvUser);
420 }
421 }
422 }
423 else
424 rc = VERR_NO_MEMORY;
425 RTMemTmpFree(pbBuf2Free);
426 RTMemTmpFree(pbBuf1Free);
427
428 return rc;
429}
430
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