VirtualBox

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

Last change on this file since 64226 was 62482, checked in by vboxsync, 8 years ago

(C) 2016

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