VirtualBox

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

Last change on this file since 105589 was 99758, checked in by vboxsync, 19 months ago

IPRT: Make doxygen 1.9.6 happy. Mostly removing duplicate docs (iprt is documented in the header files). bugref:10442

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 13.9 KB
Line 
1/* $Id: fileio.cpp 99758 2023-05-11 21:37:59Z vboxsync $ */
2/** @file
3 * IPRT - File I/O.
4 */
5
6/*
7 * Copyright (C) 2006-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 "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
212RTR3DECL(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
226RTR3DECL(RTFOFF) RTFileGetMaxSize(RTFILE File)
227{
228 RTFOFF cbMax;
229 int rc = RTFileQueryMaxSizeEx(File, &cbMax);
230 return RT_SUCCESS(rc) ? cbMax : -1;
231}
232
233
234RTDECL(int) RTFileCopyByHandles(RTFILE FileSrc, RTFILE FileDst)
235{
236 return RTFileCopyByHandlesEx(FileSrc, FileDst, NULL, NULL);
237}
238
239
240RTDECL(int) RTFileCompare(const char *pszFile1, const char *pszFile2)
241{
242 return RTFileCompareEx(pszFile1, pszFile2, 0 /*fFlags*/, NULL, NULL);
243}
244
245
246RTDECL(int) RTFileCompareByHandles(RTFILE hFile1, RTFILE hFile2)
247{
248 return RTFileCompareByHandlesEx(hFile1, hFile2, 0 /*fFlags*/, NULL, NULL);
249}
250
251
252RTDECL(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);
261 AssertPtrNullReturn(pfnProgress, VERR_INVALID_POINTER);
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,
275 RTFILE_O_READ | RTFILE_O_OPEN
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
291 int rc2 = RTFileClose(hFile1);
292 AssertRC(rc2);
293 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
294 rc = rc2;
295 }
296 return rc;
297}
298
299
300RTDECL(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);
307 AssertPtrNullReturn(pfnProgress, VERR_INVALID_POINTER);
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;
314 int rc = RTFileQuerySize(hFile1, &cbFile1);
315 if (RT_FAILURE(rc))
316 return rc;
317
318 uint64_t cbFile2;
319 rc = RTFileQuerySize(hFile1, &cbFile2);
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
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