VirtualBox

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

Last change on this file since 46757 was 46251, checked in by vboxsync, 12 years ago

Runtime: Bugfix for RTFileSg* API

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 18.1 KB
Line 
1/* $Id: fileio.cpp 46251 2013-05-24 07:38:51Z vboxsync $ */
2/** @file
3 * IPRT - File I/O.
4 */
5
6/*
7 * Copyright (C) 2006-2011 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* Header Files *
29*******************************************************************************/
30#include <iprt/file.h>
31#include <iprt/alloc.h>
32#include <iprt/assert.h>
33#include <iprt/alloca.h>
34#include <iprt/err.h>
35#include "internal/file.h"
36
37
38/*******************************************************************************
39* Global Variables *
40*******************************************************************************/
41/** Set of forced set open flags for files opened read-only. */
42static unsigned g_fOpenReadSet = 0;
43
44/** Set of forced cleared open flags for files opened read-only. */
45static unsigned g_fOpenReadMask = 0;
46
47/** Set of forced set open flags for files opened write-only. */
48static unsigned g_fOpenWriteSet = 0;
49
50/** Set of forced cleared open flags for files opened write-only. */
51static unsigned g_fOpenWriteMask = 0;
52
53/** Set of forced set open flags for files opened read-write. */
54static unsigned g_fOpenReadWriteSet = 0;
55
56/** Set of forced cleared open flags for files opened read-write. */
57static unsigned g_fOpenReadWriteMask = 0;
58
59
60/**
61 * Force the use of open flags for all files opened after the setting is
62 * changed. The caller is responsible for not causing races with RTFileOpen().
63 *
64 * @returns iprt status code.
65 * @param fOpenForAccess Access mode to which the set/mask settings apply.
66 * @param fSet Open flags to be forced set.
67 * @param fMask Open flags to be masked out.
68 */
69RTR3DECL(int) RTFileSetForceFlags(unsigned fOpenForAccess, unsigned fSet, unsigned fMask)
70{
71 /*
72 * For now allow only RTFILE_O_WRITE_THROUGH. The other flags either
73 * make no sense in this context or are not useful to apply to all files.
74 */
75 if ((fSet | fMask) & ~RTFILE_O_WRITE_THROUGH)
76 return VERR_INVALID_PARAMETER;
77 switch (fOpenForAccess)
78 {
79 case RTFILE_O_READ:
80 g_fOpenReadSet = fSet;
81 g_fOpenReadMask = fMask;
82 break;
83 case RTFILE_O_WRITE:
84 g_fOpenWriteSet = fSet;
85 g_fOpenWriteMask = fMask;
86 break;
87 case RTFILE_O_READWRITE:
88 g_fOpenReadWriteSet = fSet;
89 g_fOpenReadWriteMask = fMask;
90 break;
91 default:
92 AssertMsgFailed(("Invalid access mode %d\n", fOpenForAccess));
93 return VERR_INVALID_PARAMETER;
94 }
95 return VINF_SUCCESS;
96}
97
98
99/**
100 * Adjusts and validates the flags.
101 *
102 * The adjustments are made according to the wishes specified using the RTFileSetForceFlags API.
103 *
104 * @returns IPRT status code.
105 * @param pfOpen Pointer to the user specified flags on input.
106 * Updated on successful return.
107 * @internal
108 */
109int rtFileRecalcAndValidateFlags(uint64_t *pfOpen)
110{
111 /*
112 * Recalc.
113 */
114 uint32_t fOpen = *pfOpen;
115 switch (fOpen & RTFILE_O_ACCESS_MASK)
116 {
117 case RTFILE_O_READ:
118 fOpen |= g_fOpenReadSet;
119 fOpen &= ~g_fOpenReadMask;
120 break;
121 case RTFILE_O_WRITE:
122 fOpen |= g_fOpenWriteSet;
123 fOpen &= ~g_fOpenWriteMask;
124 break;
125 case RTFILE_O_READWRITE:
126 fOpen |= g_fOpenReadWriteSet;
127 fOpen &= ~g_fOpenReadWriteMask;
128 break;
129 default:
130 AssertMsgFailed(("Invalid RW value, fOpen=%#llx\n", fOpen));
131 return VERR_INVALID_PARAMETER;
132 }
133
134 /*
135 * Validate .
136 */
137 AssertMsgReturn(fOpen & RTFILE_O_ACCESS_MASK, ("Missing RTFILE_O_READ/WRITE: fOpen=%#llx\n", fOpen), VERR_INVALID_PARAMETER);
138#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
139 AssertMsgReturn(!(fOpen & (~(uint64_t)RTFILE_O_VALID_MASK | RTFILE_O_NON_BLOCK)), ("%#llx\n", fOpen), VERR_INVALID_PARAMETER);
140#else
141 AssertMsgReturn(!(fOpen & ~(uint64_t)RTFILE_O_VALID_MASK), ("%#llx\n", fOpen), VERR_INVALID_PARAMETER);
142#endif
143 AssertMsgReturn((fOpen & (RTFILE_O_TRUNCATE | RTFILE_O_WRITE)) != RTFILE_O_TRUNCATE, ("%#llx\n", fOpen), VERR_INVALID_PARAMETER);
144
145 switch (fOpen & RTFILE_O_ACTION_MASK)
146 {
147 case 0: /* temporarily */
148 AssertMsgFailed(("Missing RTFILE_O_OPEN/CREATE*! (continuable assertion)\n"));
149 fOpen |= RTFILE_O_OPEN;
150 break;
151 case RTFILE_O_OPEN:
152 AssertMsgReturn(!(RTFILE_O_NOT_CONTENT_INDEXED & fOpen), ("%#llx\n", fOpen), VERR_INVALID_PARAMETER);
153 case RTFILE_O_OPEN_CREATE:
154 case RTFILE_O_CREATE:
155 case RTFILE_O_CREATE_REPLACE:
156 break;
157 default:
158 AssertMsgFailed(("Invalid action value: fOpen=%#llx\n", fOpen));
159 return VERR_INVALID_PARAMETER;
160 }
161
162 switch (fOpen & RTFILE_O_DENY_MASK)
163 {
164 case 0: /* temporarily */
165 AssertMsgFailed(("Missing RTFILE_O_DENY_*! (continuable assertion)\n"));
166 fOpen |= RTFILE_O_DENY_NONE;
167 break;
168 case RTFILE_O_DENY_NONE:
169 case RTFILE_O_DENY_READ:
170 case RTFILE_O_DENY_WRITE:
171 case RTFILE_O_DENY_WRITE | RTFILE_O_DENY_READ:
172 case RTFILE_O_DENY_NOT_DELETE:
173 case RTFILE_O_DENY_NOT_DELETE | RTFILE_O_DENY_READ:
174 case RTFILE_O_DENY_NOT_DELETE | RTFILE_O_DENY_WRITE:
175 case RTFILE_O_DENY_NOT_DELETE | RTFILE_O_DENY_WRITE | RTFILE_O_DENY_READ:
176 break;
177 default:
178 AssertMsgFailed(("Invalid deny value: fOpen=%#llx\n", fOpen));
179 return VERR_INVALID_PARAMETER;
180 }
181
182 /* done */
183 *pfOpen = fOpen;
184 return VINF_SUCCESS;
185}
186
187
188
189/**
190 * Read bytes from a file at a given offset.
191 * This function may modify the file position.
192 *
193 * @returns iprt status code.
194 * @param File Handle to the file.
195 * @param off Where to read.
196 * @param pvBuf Where to put the bytes we read.
197 * @param cbToRead How much to read.
198 * @param *pcbRead How much we actually read.
199 * If NULL an error will be returned for a partial read.
200 */
201RTR3DECL(int) RTFileReadAt(RTFILE File, RTFOFF off, void *pvBuf, size_t cbToRead, size_t *pcbRead)
202{
203 int rc = RTFileSeek(File, off, RTFILE_SEEK_BEGIN, NULL);
204 if (RT_SUCCESS(rc))
205 rc = RTFileRead(File, pvBuf, cbToRead, pcbRead);
206 return rc;
207}
208
209
210/**
211 * Read bytes from a file at a given offset into a S/G buffer.
212 * This function may modify the file position.
213 *
214 * @returns iprt status code.
215 * @param hFile Handle to the file.
216 * @param off Where to read.
217 * @param pSgBuf Pointer to the S/G buffer to read into.
218 * @param cbToRead How much to read.
219 * @param pcbRead How much we actually read.
220 * If NULL an error will be returned for a partial read.
221 */
222RTR3DECL(int) RTFileSgReadAt(RTFILE hFile, RTFOFF off, PRTSGBUF pSgBuf, size_t cbToRead, size_t *pcbRead)
223{
224 int rc = VINF_SUCCESS;
225 size_t cbRead = 0;
226
227 while (cbToRead)
228 {
229 size_t cbThisRead = 0;
230 size_t cbBuf = cbToRead;
231 void *pvBuf = RTSgBufGetNextSegment(pSgBuf, &cbBuf);
232
233 rc = RTFileReadAt(hFile, off, pvBuf, cbBuf, pcbRead ? &cbThisRead : NULL);
234 if (RT_SUCCESS(rc))
235 cbRead += cbThisRead;
236
237 if ( RT_FAILURE(rc)
238 || ( cbThisRead < cbBuf
239 && pcbRead))
240 break;
241
242 cbToRead -= cbBuf;
243 off += cbBuf;
244 }
245
246 if (pcbRead)
247 *pcbRead = cbRead;
248
249 return rc;
250}
251
252
253/**
254 * Write bytes to a file at a given offset.
255 * This function may modify the file position.
256 *
257 * @returns iprt status code.
258 * @param File Handle to the file.
259 * @param off Where to write.
260 * @param pvBuf What to write.
261 * @param cbToWrite How much to write.
262 * @param *pcbWritten How much we actually wrote.
263 * If NULL an error will be returned for a partial write.
264 */
265RTR3DECL(int) RTFileWriteAt(RTFILE File, RTFOFF off, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
266{
267 int rc = RTFileSeek(File, off, RTFILE_SEEK_BEGIN, NULL);
268 if (RT_SUCCESS(rc))
269 rc = RTFileWrite(File, pvBuf, cbToWrite, pcbWritten);
270 return rc;
271}
272
273
274/**
275 * Write bytes from a S/G buffer to a file at a given offset.
276 * This function may modify the file position.
277 *
278 * @returns iprt status code.
279 * @param hFile Handle to the file.
280 * @param off Where to write.
281 * @param pSgBuf What to write.
282 * @param cbToWrite How much to write.
283 * @param pcbWritten How much we actually wrote.
284 * If NULL an error will be returned for a partial write.
285 */
286RTR3DECL(int) RTFileSgWriteAt(RTFILE hFile, RTFOFF off, PRTSGBUF pSgBuf, size_t cbToWrite, size_t *pcbWritten)
287{
288 int rc = VINF_SUCCESS;
289 size_t cbWritten = 0;
290
291 while (cbToWrite)
292 {
293 size_t cbThisWritten = 0;
294 size_t cbBuf = cbToWrite;
295 void *pvBuf = RTSgBufGetNextSegment(pSgBuf, &cbBuf);
296
297 rc = RTFileWriteAt(hFile, off, pvBuf, cbBuf, pcbWritten ? &cbThisWritten : NULL);
298 if (RT_SUCCESS(rc))
299 cbWritten += cbThisWritten;
300
301 if ( RT_FAILURE(rc)
302 || ( cbThisWritten < cbBuf
303 && pcbWritten))
304 break;
305
306 cbToWrite -= cbBuf;
307 off += cbBuf;
308 }
309
310 if (pcbWritten)
311 *pcbWritten = cbWritten;
312
313 return rc;
314}
315
316
317/**
318 * Gets the current file position.
319 *
320 * @returns File offset.
321 * @returns ~0UUL on failure.
322 * @param File File handle.
323 */
324RTR3DECL(uint64_t) RTFileTell(RTFILE File)
325{
326 /*
327 * Call the seek api to query the stuff.
328 */
329 uint64_t off = 0;
330 int rc = RTFileSeek(File, 0, RTFILE_SEEK_CURRENT, &off);
331 if (RT_SUCCESS(rc))
332 return off;
333 AssertMsgFailed(("RTFileSeek(%d) -> %d\n", File, rc));
334 return ~0ULL;
335}
336
337
338/**
339 * Determine the maximum file size.
340 *
341 * @returns The max size of the file.
342 * -1 on failure, the file position is undefined.
343 * @param File Handle to the file.
344 * @see RTFileGetMaxSizeEx.
345 */
346RTR3DECL(RTFOFF) RTFileGetMaxSize(RTFILE File)
347{
348 RTFOFF cbMax;
349 int rc = RTFileGetMaxSizeEx(File, &cbMax);
350 return RT_SUCCESS(rc) ? cbMax : -1;
351}
352
353
354/**
355 * Copies a file given the handles to both files.
356 *
357 * @returns VBox Status code.
358 *
359 * @param FileSrc The source file. The file position is unaltered.
360 * @param FileDst The destination file.
361 * On successful returns the file position is at the end of the file.
362 * On failures the file position and size is undefined.
363 */
364RTDECL(int) RTFileCopyByHandles(RTFILE FileSrc, RTFILE FileDst)
365{
366 return RTFileCopyByHandlesEx(FileSrc, FileDst, NULL, NULL);
367}
368
369
370/**
371 * Copies a file.
372 *
373 * @returns VERR_ALREADY_EXISTS if the destination file exists.
374 * @returns VBox Status code.
375 *
376 * @param pszSrc The path to the source file.
377 * @param pszDst The path to the destination file.
378 * This file will be created.
379 * @param fFlags Flags, any of the RTFILECOPY_FLAGS_ \#defines.
380 * @param pfnProgress Pointer to callback function for reporting progress.
381 * @param pvUser User argument to pass to pfnProgress along with the completion percentage.
382 */
383RTDECL(int) RTFileCopyEx(const char *pszSrc, const char *pszDst, uint32_t fFlags, PFNRTPROGRESS pfnProgress, void *pvUser)
384{
385 /*
386 * Validate input.
387 */
388 AssertMsgReturn(VALID_PTR(pszSrc), ("pszSrc=%p\n", pszSrc), VERR_INVALID_PARAMETER);
389 AssertMsgReturn(*pszSrc, ("pszSrc=%p\n", pszSrc), VERR_INVALID_PARAMETER);
390 AssertMsgReturn(VALID_PTR(pszDst), ("pszDst=%p\n", pszDst), VERR_INVALID_PARAMETER);
391 AssertMsgReturn(*pszDst, ("pszDst=%p\n", pszDst), VERR_INVALID_PARAMETER);
392 AssertMsgReturn(!pfnProgress || VALID_PTR(pfnProgress), ("pfnProgress=%p\n", pfnProgress), VERR_INVALID_PARAMETER);
393 AssertMsgReturn(!(fFlags & ~RTFILECOPY_FLAGS_MASK), ("%#x\n", fFlags), VERR_INVALID_PARAMETER);
394
395 /*
396 * Open the files.
397 */
398 RTFILE FileSrc;
399 int rc = RTFileOpen(&FileSrc, pszSrc,
400 RTFILE_O_READ | RTFILE_O_OPEN
401 | (fFlags & RTFILECOPY_FLAGS_NO_SRC_DENY_WRITE ? RTFILE_O_DENY_NONE : RTFILE_O_DENY_WRITE));
402 if (RT_SUCCESS(rc))
403 {
404 RTFILE FileDst;
405 rc = RTFileOpen(&FileDst, pszDst,
406 RTFILE_O_WRITE | RTFILE_O_CREATE
407 | (fFlags & RTFILECOPY_FLAGS_NO_DST_DENY_WRITE ? RTFILE_O_DENY_NONE : RTFILE_O_DENY_WRITE));
408 if (RT_SUCCESS(rc))
409 {
410 /*
411 * Call the ByHandles version and let it do the job.
412 */
413 rc = RTFileCopyByHandlesEx(FileSrc, FileDst, pfnProgress, pvUser);
414
415 /*
416 * Close the files regardless of the result.
417 * Don't bother cleaning up or anything like that.
418 */
419 int rc2 = RTFileClose(FileDst);
420 AssertRC(rc2);
421 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
422 rc = rc2;
423 }
424
425 int rc2 = RTFileClose(FileSrc);
426 AssertRC(rc2);
427 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
428 rc = rc2;
429 }
430 return rc;
431}
432
433
434/**
435 * Copies a file given the handles to both files and
436 * provide progress callbacks.
437 *
438 * @returns VBox Status code.
439 *
440 * @param FileSrc The source file. The file position is unaltered.
441 * @param FileDst The destination file.
442 * On successful returns the file position is at the end of the file.
443 * On failures the file position and size is undefined.
444 * @param pfnProgress Pointer to callback function for reporting progress.
445 * @param pvUser User argument to pass to pfnProgress along with the completion percentage.
446 */
447RTDECL(int) RTFileCopyByHandlesEx(RTFILE FileSrc, RTFILE FileDst, PFNRTPROGRESS pfnProgress, void *pvUser)
448{
449 /*
450 * Validate input.
451 */
452 AssertMsgReturn(RTFileIsValid(FileSrc), ("FileSrc=%RTfile\n", FileSrc), VERR_INVALID_PARAMETER);
453 AssertMsgReturn(RTFileIsValid(FileDst), ("FileDst=%RTfile\n", FileDst), VERR_INVALID_PARAMETER);
454 AssertMsgReturn(!pfnProgress || VALID_PTR(pfnProgress), ("pfnProgress=%p\n", pfnProgress), VERR_INVALID_PARAMETER);
455
456 /*
457 * Save file offset.
458 */
459 RTFOFF offSrcSaved;
460 int rc = RTFileSeek(FileSrc, 0, RTFILE_SEEK_CURRENT, (uint64_t *)&offSrcSaved);
461 if (RT_FAILURE(rc))
462 return rc;
463
464 /*
465 * Get the file size.
466 */
467 RTFOFF cbSrc;
468 rc = RTFileSeek(FileSrc, 0, RTFILE_SEEK_END, (uint64_t *)&cbSrc);
469 if (RT_FAILURE(rc))
470 return rc;
471
472 /*
473 * Allocate buffer.
474 */
475 size_t cbBuf;
476 uint8_t *pbBufFree = NULL;
477 uint8_t *pbBuf;
478 if (cbSrc < _512K)
479 {
480 cbBuf = 8*_1K;
481 pbBuf = (uint8_t *)alloca(cbBuf);
482 }
483 else
484 {
485 cbBuf = _128K;
486 pbBuf = pbBufFree = (uint8_t *)RTMemTmpAlloc(cbBuf);
487 }
488 if (pbBuf)
489 {
490 /*
491 * Seek to the start of each file
492 * and set the size of the destination file.
493 */
494 rc = RTFileSeek(FileSrc, 0, RTFILE_SEEK_BEGIN, NULL);
495 if (RT_SUCCESS(rc))
496 {
497 rc = RTFileSeek(FileDst, 0, RTFILE_SEEK_BEGIN, NULL);
498 if (RT_SUCCESS(rc))
499 rc = RTFileSetSize(FileDst, cbSrc);
500 if (RT_SUCCESS(rc) && pfnProgress)
501 rc = pfnProgress(0, pvUser);
502 if (RT_SUCCESS(rc))
503 {
504 /*
505 * Copy loop.
506 */
507 unsigned uPercentage = 0;
508 RTFOFF off = 0;
509 RTFOFF cbPercent = cbSrc / 100;
510 RTFOFF offNextPercent = cbPercent;
511 while (off < cbSrc)
512 {
513 /* copy block */
514 RTFOFF cbLeft = cbSrc - off;
515 size_t cbBlock = cbLeft >= (RTFOFF)cbBuf ? cbBuf : (size_t)cbLeft;
516 rc = RTFileRead(FileSrc, pbBuf, cbBlock, NULL);
517 if (RT_FAILURE(rc))
518 break;
519 rc = RTFileWrite(FileDst, pbBuf, cbBlock, NULL);
520 if (RT_FAILURE(rc))
521 break;
522
523 /* advance */
524 off += cbBlock;
525 if (pfnProgress && offNextPercent < off)
526 {
527 while (offNextPercent < off)
528 {
529 uPercentage++;
530 offNextPercent += cbPercent;
531 }
532 rc = pfnProgress(uPercentage, pvUser);
533 if (RT_FAILURE(rc))
534 break;
535 }
536 }
537
538#if 0
539 /*
540 * Copy OS specific data (EAs and stuff).
541 */
542 rtFileCopyOSStuff(FileSrc, FileDst);
543#endif
544
545 /* 100% */
546 if (pfnProgress && uPercentage < 100 && RT_SUCCESS(rc))
547 rc = pfnProgress(100, pvUser);
548 }
549 }
550 RTMemTmpFree(pbBufFree);
551 }
552 else
553 rc = VERR_NO_MEMORY;
554
555 /*
556 * Restore source position.
557 */
558 RTFileSeek(FileSrc, offSrcSaved, RTFILE_SEEK_BEGIN, NULL);
559
560 return rc;
561}
562
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