VirtualBox

source: vbox/trunk/src/VBox/Storage/VDVfs.cpp@ 52902

Last change on this file since 52902 was 43121, checked in by vboxsync, 12 years ago

CDDL licensing cleanup

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.2 KB
Line 
1/* $Id: VDVfs.cpp 43121 2012-08-30 18:36:48Z vboxsync $ */
2/** @file
3 * Virtual Disk Container implementation. - VFS glue.
4 */
5
6/*
7 * Copyright (C) 2012 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
18
19/*******************************************************************************
20* Header Files *
21*******************************************************************************/
22#include <iprt/types.h>
23#include <iprt/assert.h>
24#include <iprt/mem.h>
25#include <iprt/err.h>
26#include <iprt/asm.h>
27#include <iprt/string.h>
28#include <iprt/file.h>
29#include <iprt/sg.h>
30#include <iprt/vfslowlevel.h>
31#include <iprt/poll.h>
32#include <VBox/vd.h>
33
34/*******************************************************************************
35* Structures and Typedefs *
36*******************************************************************************/
37
38/**
39 * The internal data of a DVM volume I/O stream.
40 */
41typedef struct VDVFSFILE
42{
43 /** The volume the VFS file belongs to. */
44 PVBOXHDD pDisk;
45 /** Current position. */
46 uint64_t offCurPos;
47 /** Flags given during creation. */
48 uint32_t fFlags;
49} VDVFSFILE;
50/** Pointer to a the internal data of a DVM volume file. */
51typedef VDVFSFILE *PVDVFSFILE;
52
53/**
54 * VD read helper taking care of unaligned accesses.
55 *
56 * @return VBox status code.
57 * @param pDisk VD disk container.
58 * @param off Offset to start reading from.
59 * @param pvBuf Pointer to the buffer to read into.
60 * @param cbRead Amount of bytes to read.
61 */
62static int vdReadHelper(PVBOXHDD pDisk, uint64_t off, void *pvBuf, size_t cbRead)
63{
64 int rc = VINF_SUCCESS;
65
66 /* Take shortcut if possible. */
67 if ( off % 512 == 0
68 && cbRead % 512 == 0)
69 rc = VDRead(pDisk, off, pvBuf, cbRead);
70 else
71 {
72 uint8_t *pbBuf = (uint8_t *)pvBuf;
73 uint8_t abBuf[512];
74
75 /* Unaligned access, make it aligned. */
76 if (off % 512 != 0)
77 {
78 uint64_t offAligned = off & ~(uint64_t)(512 - 1);
79 size_t cbToCopy = 512 - (off - offAligned);
80 rc = VDRead(pDisk, offAligned, abBuf, 512);
81 if (RT_SUCCESS(rc))
82 {
83 memcpy(pbBuf, &abBuf[off - offAligned], cbToCopy);
84 pbBuf += cbToCopy;
85 off += cbToCopy;
86 cbRead -= cbToCopy;
87 }
88 }
89
90 if ( RT_SUCCESS(rc)
91 && (cbRead & ~(uint64_t)(512 - 1)))
92 {
93 size_t cbReadAligned = cbRead & ~(uint64_t)(512 - 1);
94
95 Assert(!(off % 512));
96 rc = VDRead(pDisk, off, pbBuf, cbReadAligned);
97 if (RT_SUCCESS(rc))
98 {
99 pbBuf += cbReadAligned;
100 off += cbReadAligned;
101 cbRead -= cbReadAligned;
102 }
103 }
104
105 if ( RT_SUCCESS(rc)
106 && cbRead)
107 {
108 Assert(cbRead < 512);
109 Assert(!(off % 512));
110
111 rc = VDRead(pDisk, off, abBuf, 512);
112 if (RT_SUCCESS(rc))
113 memcpy(pbBuf, abBuf, cbRead);
114 }
115 }
116
117 return rc;
118}
119
120
121/**
122 * VD write helper taking care of unaligned accesses.
123 *
124 * @return VBox status code.
125 * @param pDisk VD disk container.
126 * @param off Offset to start writing to.
127 * @param pvBuf Pointer to the buffer to read from.
128 * @param cbWrite Amount of bytes to write.
129 */
130static int vdWriteHelper(PVBOXHDD pDisk, uint64_t off, const void *pvBuf, size_t cbWrite)
131{
132 int rc = VINF_SUCCESS;
133
134 /* Take shortcut if possible. */
135 if ( off % 512 == 0
136 && cbWrite % 512 == 0)
137 rc = VDWrite(pDisk, off, pvBuf, cbWrite);
138 else
139 {
140 uint8_t *pbBuf = (uint8_t *)pvBuf;
141 uint8_t abBuf[512];
142
143 /* Unaligned access, make it aligned. */
144 if (off % 512 != 0)
145 {
146 uint64_t offAligned = off & ~(uint64_t)(512 - 1);
147 size_t cbToCopy = 512 - (off - offAligned);
148 rc = VDRead(pDisk, offAligned, abBuf, 512);
149 if (RT_SUCCESS(rc))
150 {
151 memcpy(&abBuf[off - offAligned], pbBuf, cbToCopy);
152 rc = VDWrite(pDisk, offAligned, abBuf, 512);
153
154 pbBuf += cbToCopy;
155 off += cbToCopy;
156 cbWrite -= cbToCopy;
157 }
158 }
159
160 if ( RT_SUCCESS(rc)
161 && (cbWrite & ~(uint64_t)(512 - 1)))
162 {
163 size_t cbWriteAligned = cbWrite & ~(uint64_t)(512 - 1);
164
165 Assert(!(off % 512));
166 rc = VDWrite(pDisk, off, pbBuf, cbWriteAligned);
167 if (RT_SUCCESS(rc))
168 {
169 pbBuf += cbWriteAligned;
170 off += cbWriteAligned;
171 cbWrite -= cbWriteAligned;
172 }
173 }
174
175 if ( RT_SUCCESS(rc)
176 && cbWrite)
177 {
178 Assert(cbWrite < 512);
179 Assert(!(off % 512));
180
181 rc = VDRead(pDisk, off, abBuf, 512);
182 if (RT_SUCCESS(rc))
183 {
184 memcpy(abBuf, pbBuf, cbWrite);
185 rc = VDWrite(pDisk, off, abBuf, 512);
186 }
187 }
188 }
189
190 return rc;
191}
192
193
194/**
195 * @interface_method_impl{RTVFSOBJOPS,pfnClose}
196 */
197static DECLCALLBACK(int) vdVfsFile_Close(void *pvThis)
198{
199 PVDVFSFILE pThis = (PVDVFSFILE)pvThis;
200
201 if (pThis->fFlags & VD_VFSFILE_DESTROY_ON_RELEASE)
202 VDDestroy(pThis->pDisk);
203
204 return VINF_SUCCESS;
205}
206
207
208/**
209 * @interface_method_impl{RTVFSOBJOPS,pfnQueryInfo}
210 */
211static DECLCALLBACK(int) vdVfsFile_QueryInfo(void *pvThis, PRTFSOBJINFO pObjInfo,
212 RTFSOBJATTRADD enmAddAttr)
213{
214 NOREF(pvThis);
215 NOREF(pObjInfo);
216 NOREF(enmAddAttr);
217 return VERR_NOT_SUPPORTED;
218}
219
220
221/**
222 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnRead}
223 */
224static DECLCALLBACK(int) vdVfsFile_Read(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbRead)
225{
226 PVDVFSFILE pThis = (PVDVFSFILE)pvThis;
227 int rc = VINF_SUCCESS;
228
229 Assert(pSgBuf->cSegs == 1);
230 NOREF(fBlocking);
231
232 /*
233 * Find the current position and check if it's within the volume.
234 */
235 uint64_t offUnsigned = off < 0 ? pThis->offCurPos : (uint64_t)off;
236 if (offUnsigned >= VDGetSize(pThis->pDisk, VD_LAST_IMAGE))
237 {
238 if (pcbRead)
239 {
240 *pcbRead = 0;
241 pThis->offCurPos = offUnsigned;
242 return VINF_EOF;
243 }
244 return VERR_EOF;
245 }
246
247 size_t cbLeftToRead;
248 if (offUnsigned + pSgBuf->paSegs[0].cbSeg > VDGetSize(pThis->pDisk, VD_LAST_IMAGE))
249 {
250 if (!pcbRead)
251 return VERR_EOF;
252 *pcbRead = cbLeftToRead = (size_t)(VDGetSize(pThis->pDisk, VD_LAST_IMAGE) - offUnsigned);
253 }
254 else
255 {
256 cbLeftToRead = pSgBuf->paSegs[0].cbSeg;
257 if (pcbRead)
258 *pcbRead = cbLeftToRead;
259 }
260
261 /*
262 * Ok, we've got a valid stretch within the file. Do the reading.
263 */
264 if (cbLeftToRead > 0)
265 {
266 rc = vdReadHelper(pThis->pDisk, (uint64_t)off, pSgBuf->paSegs[0].pvSeg, cbLeftToRead);
267 if (RT_SUCCESS(rc))
268 offUnsigned += cbLeftToRead;
269 }
270
271 pThis->offCurPos = offUnsigned;
272 return rc;
273}
274
275
276/**
277 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnWrite}
278 */
279static DECLCALLBACK(int) vdVfsFile_Write(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbWritten)
280{
281 PVDVFSFILE pThis = (PVDVFSFILE)pvThis;
282 int rc = VINF_SUCCESS;
283
284 Assert(pSgBuf->cSegs == 1);
285 NOREF(fBlocking);
286
287 /*
288 * Find the current position and check if it's within the volume.
289 * Writing beyond the end of a volume is not supported.
290 */
291 uint64_t offUnsigned = off < 0 ? pThis->offCurPos : (uint64_t)off;
292 if (offUnsigned >= VDGetSize(pThis->pDisk, VD_LAST_IMAGE))
293 {
294 if (pcbWritten)
295 {
296 *pcbWritten = 0;
297 pThis->offCurPos = offUnsigned;
298 }
299 return VERR_NOT_SUPPORTED;
300 }
301
302 size_t cbLeftToWrite;
303 if (offUnsigned + pSgBuf->paSegs[0].cbSeg > VDGetSize(pThis->pDisk, VD_LAST_IMAGE))
304 {
305 if (!pcbWritten)
306 return VERR_EOF;
307 *pcbWritten = cbLeftToWrite = (size_t)(VDGetSize(pThis->pDisk, VD_LAST_IMAGE) - offUnsigned);
308 }
309 else
310 {
311 cbLeftToWrite = pSgBuf->paSegs[0].cbSeg;
312 if (pcbWritten)
313 *pcbWritten = cbLeftToWrite;
314 }
315
316 /*
317 * Ok, we've got a valid stretch within the file. Do the reading.
318 */
319 if (cbLeftToWrite > 0)
320 {
321 rc = vdWriteHelper(pThis->pDisk, (uint64_t)off, pSgBuf->paSegs[0].pvSeg, cbLeftToWrite);
322 if (RT_SUCCESS(rc))
323 offUnsigned += cbLeftToWrite;
324 }
325
326 pThis->offCurPos = offUnsigned;
327 return rc;
328}
329
330
331/**
332 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnFlush}
333 */
334static DECLCALLBACK(int) vdVfsFile_Flush(void *pvThis)
335{
336 PVDVFSFILE pThis = (PVDVFSFILE)pvThis;
337 return VDFlush(pThis->pDisk);
338}
339
340
341/**
342 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnPollOne}
343 */
344static DECLCALLBACK(int) vdVfsFile_PollOne(void *pvThis, uint32_t fEvents, RTMSINTERVAL cMillies, bool fIntr,
345 uint32_t *pfRetEvents)
346{
347 NOREF(pvThis);
348 int rc;
349 if (fEvents != RTPOLL_EVT_ERROR)
350 {
351 *pfRetEvents = fEvents & ~RTPOLL_EVT_ERROR;
352 rc = VINF_SUCCESS;
353 }
354 else
355 rc = RTVfsUtilDummyPollOne(fEvents, cMillies, fIntr, pfRetEvents);
356 return rc;
357}
358
359
360/**
361 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnTell}
362 */
363static DECLCALLBACK(int) vdVfsFile_Tell(void *pvThis, PRTFOFF poffActual)
364{
365 PVDVFSFILE pThis = (PVDVFSFILE)pvThis;
366 *poffActual = pThis->offCurPos;
367 return VINF_SUCCESS;
368}
369
370
371/**
372 * @interface_method_impl{RTVFSOBJSETOPS,pfnMode}
373 */
374static DECLCALLBACK(int) vdVfsFile_SetMode(void *pvThis, RTFMODE fMode, RTFMODE fMask)
375{
376 NOREF(pvThis);
377 NOREF(fMode);
378 NOREF(fMask);
379 return VERR_NOT_SUPPORTED;
380}
381
382
383/**
384 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetTimes}
385 */
386static DECLCALLBACK(int) vdVfsFile_SetTimes(void *pvThis, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
387 PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
388{
389 NOREF(pvThis);
390 NOREF(pAccessTime);
391 NOREF(pModificationTime);
392 NOREF(pChangeTime);
393 NOREF(pBirthTime);
394 return VERR_NOT_SUPPORTED;
395}
396
397
398/**
399 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetOwner}
400 */
401static DECLCALLBACK(int) vdVfsFile_SetOwner(void *pvThis, RTUID uid, RTGID gid)
402{
403 NOREF(pvThis);
404 NOREF(uid);
405 NOREF(gid);
406 return VERR_NOT_SUPPORTED;
407}
408
409
410/**
411 * @interface_method_impl{RTVFSFILEOPS,pfnSeek}
412 */
413static DECLCALLBACK(int) vdVfsFile_Seek(void *pvThis, RTFOFF offSeek, unsigned uMethod, PRTFOFF poffActual)
414{
415 PVDVFSFILE pThis = (PVDVFSFILE)pvThis;
416
417 /*
418 * Seek relative to which position.
419 */
420 uint64_t offWrt;
421 switch (uMethod)
422 {
423 case RTFILE_SEEK_BEGIN:
424 offWrt = 0;
425 break;
426
427 case RTFILE_SEEK_CURRENT:
428 offWrt = pThis->offCurPos;
429 break;
430
431 case RTFILE_SEEK_END:
432 offWrt = VDGetSize(pThis->pDisk, VD_LAST_IMAGE);
433 break;
434
435 default:
436 return VERR_INTERNAL_ERROR_5;
437 }
438
439 /*
440 * Calc new position, take care to stay within bounds.
441 *
442 * @todo: Setting position beyond the end of the disk does not make sense.
443 */
444 uint64_t offNew;
445 if (offSeek == 0)
446 offNew = offWrt;
447 else if (offSeek > 0)
448 {
449 offNew = offWrt + offSeek;
450 if ( offNew < offWrt
451 || offNew > RTFOFF_MAX)
452 offNew = RTFOFF_MAX;
453 }
454 else if ((uint64_t)-offSeek < offWrt)
455 offNew = offWrt + offSeek;
456 else
457 offNew = 0;
458
459 /*
460 * Update the state and set return value.
461 */
462 pThis->offCurPos = offNew;
463
464 *poffActual = offNew;
465 return VINF_SUCCESS;
466}
467
468
469/**
470 * @interface_method_impl{RTVFSFILEOPS,pfnQuerySize}
471 */
472static DECLCALLBACK(int) vdVfsFile_QuerySize(void *pvThis, uint64_t *pcbFile)
473{
474 PVDVFSFILE pThis = (PVDVFSFILE)pvThis;
475 *pcbFile = VDGetSize(pThis->pDisk, VD_LAST_IMAGE);
476 return VINF_SUCCESS;
477}
478
479
480/**
481 * Standard file operations.
482 */
483DECL_HIDDEN_CONST(const RTVFSFILEOPS) g_vdVfsStdFileOps =
484{
485 { /* Stream */
486 { /* Obj */
487 RTVFSOBJOPS_VERSION,
488 RTVFSOBJTYPE_FILE,
489 "VDFile",
490 vdVfsFile_Close,
491 vdVfsFile_QueryInfo,
492 RTVFSOBJOPS_VERSION
493 },
494 RTVFSIOSTREAMOPS_VERSION,
495 RTVFSIOSTREAMOPS_FEAT_NO_SG,
496 vdVfsFile_Read,
497 vdVfsFile_Write,
498 vdVfsFile_Flush,
499 vdVfsFile_PollOne,
500 vdVfsFile_Tell,
501 NULL /*Skip*/,
502 NULL /*ZeroFill*/,
503 RTVFSIOSTREAMOPS_VERSION,
504 },
505 RTVFSFILEOPS_VERSION,
506 /*RTVFSIOFILEOPS_FEAT_NO_AT_OFFSET*/ 0,
507 { /* ObjSet */
508 RTVFSOBJSETOPS_VERSION,
509 RT_OFFSETOF(RTVFSFILEOPS, Stream.Obj) - RT_OFFSETOF(RTVFSFILEOPS, ObjSet),
510 vdVfsFile_SetMode,
511 vdVfsFile_SetTimes,
512 vdVfsFile_SetOwner,
513 RTVFSOBJSETOPS_VERSION
514 },
515 vdVfsFile_Seek,
516 vdVfsFile_QuerySize,
517 RTVFSFILEOPS_VERSION
518};
519
520
521VBOXDDU_DECL(int) VDCreateVfsFileFromDisk(PVBOXHDD pDisk, uint32_t fFlags,
522 PRTVFSFILE phVfsFile)
523{
524 AssertPtrReturn(pDisk, VERR_INVALID_HANDLE);
525 AssertPtrReturn(phVfsFile, VERR_INVALID_POINTER);
526 AssertReturn((fFlags & ~VD_VFSFILE_FLAGS_MASK) == 0, VERR_INVALID_PARAMETER);
527
528 /*
529 * Create the volume file.
530 */
531 RTVFSFILE hVfsFile;
532 PVDVFSFILE pThis;
533 int rc = RTVfsNewFile(&g_vdVfsStdFileOps, sizeof(*pThis), RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_WRITE,
534 NIL_RTVFS, NIL_RTVFSLOCK, &hVfsFile, (void **)&pThis);
535 if (RT_SUCCESS(rc))
536 {
537 pThis->offCurPos = 0;
538 pThis->pDisk = pDisk;
539 pThis->fFlags = fFlags;
540
541 *phVfsFile = hVfsFile;
542 return VINF_SUCCESS;
543 }
544
545 return rc;
546}
547
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