VirtualBox

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

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

Storage: Add VFS file glue for the VD library

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