VirtualBox

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

Last change on this file since 67954 was 67207, checked in by vboxsync, 7 years ago

Storage/VDVfs.cpp: Faked up a working query function. Some adjustments.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 15.9 KB
Line 
1/* $Id: VDVfs.cpp 67207 2017-06-01 13:16:02Z 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 PVDISK 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(PVDISK 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(PVDISK 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, RTFSOBJATTRADD enmAddAttr)
213{
214 PVDVFSFILE pThis = (PVDVFSFILE)pvThis;
215 unsigned const cOpenImages = VDGetCount(pThis->pDisk);
216
217 pObjInfo->cbObject = VDGetSize(pThis->pDisk, cOpenImages - 1);
218 pObjInfo->cbAllocated = 0;
219 for (unsigned iImage = 0; iImage < cOpenImages; iImage++)
220 pObjInfo->cbAllocated += VDGetFileSize(pThis->pDisk, iImage);
221
222 /** @todo enumerate the disk images directly... */
223 RTTimeNow(&pObjInfo->AccessTime);
224 pObjInfo->BirthTime = pObjInfo->AccessTime;
225 pObjInfo->ChangeTime = pObjInfo->AccessTime;
226 pObjInfo->ModificationTime = pObjInfo->AccessTime;
227
228 pObjInfo->Attr.fMode = RTFS_DOS_NT_NORMAL | RTFS_TYPE_FILE | 0644;
229 pObjInfo->Attr.enmAdditional = enmAddAttr;
230 switch (enmAddAttr)
231 {
232 case RTFSOBJATTRADD_UNIX:
233 pObjInfo->Attr.u.Unix.uid = NIL_RTUID;
234 pObjInfo->Attr.u.Unix.gid = NIL_RTGID;
235 pObjInfo->Attr.u.Unix.cHardlinks = 1;
236 pObjInfo->Attr.u.Unix.INodeIdDevice = 0;
237 pObjInfo->Attr.u.Unix.INodeId = 0;
238 pObjInfo->Attr.u.Unix.fFlags = 0;
239 pObjInfo->Attr.u.Unix.GenerationId = 0;
240 pObjInfo->Attr.u.Unix.Device = 0;
241 break;
242
243 case RTFSOBJATTRADD_UNIX_OWNER:
244 pObjInfo->Attr.u.UnixOwner.uid = NIL_RTUID;
245 pObjInfo->Attr.u.UnixOwner.szName[0] = '\0';
246 break;
247 case RTFSOBJATTRADD_UNIX_GROUP:
248 pObjInfo->Attr.u.UnixGroup.gid = NIL_RTGID;
249 pObjInfo->Attr.u.UnixGroup.szName[0] = '\0';
250 break;
251 case RTFSOBJATTRADD_EASIZE:
252 pObjInfo->Attr.u.EASize.cb = 0;
253 break;
254
255 default:
256 AssertFailedReturn(VERR_INVALID_PARAMETER);
257 }
258
259 return VINF_SUCCESS;
260}
261
262
263/**
264 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnRead}
265 */
266static DECLCALLBACK(int) vdVfsFile_Read(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbRead)
267{
268 PVDVFSFILE pThis = (PVDVFSFILE)pvThis;
269
270 Assert(pSgBuf->cSegs == 1);
271 NOREF(fBlocking);
272
273 /*
274 * Find the current position and check if it's within the volume.
275 */
276 uint64_t offUnsigned = off < 0 ? pThis->offCurPos : (uint64_t)off;
277 uint64_t const cbImage = VDGetSize(pThis->pDisk, VD_LAST_IMAGE);
278 if (offUnsigned >= cbImage)
279 {
280 if (pcbRead)
281 {
282 *pcbRead = 0;
283 pThis->offCurPos = cbImage;
284 return VINF_EOF;
285 }
286 return VERR_EOF;
287 }
288
289 int rc = VINF_SUCCESS;
290 size_t cbLeftToRead = pSgBuf->paSegs[0].cbSeg;
291 if (offUnsigned + cbLeftToRead <= cbImage)
292 {
293 if (pcbRead)
294 *pcbRead = cbLeftToRead;
295 }
296 else
297 {
298 if (!pcbRead)
299 return VERR_EOF;
300 *pcbRead = cbLeftToRead = (size_t)(cbImage - offUnsigned);
301 rc = VINF_EOF;
302 }
303
304 /*
305 * Ok, we've got a valid stretch within the file. Do the reading.
306 */
307 if (cbLeftToRead > 0)
308 {
309 int rc2 = vdReadHelper(pThis->pDisk, offUnsigned, pSgBuf->paSegs[0].pvSeg, cbLeftToRead);
310 if (RT_SUCCESS(rc2))
311 offUnsigned += cbLeftToRead;
312 else
313 rc = rc2;
314 }
315
316 pThis->offCurPos = offUnsigned;
317 return rc;
318}
319
320
321/**
322 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnWrite}
323 */
324static DECLCALLBACK(int) vdVfsFile_Write(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbWritten)
325{
326 PVDVFSFILE pThis = (PVDVFSFILE)pvThis;
327
328 Assert(pSgBuf->cSegs == 1);
329 NOREF(fBlocking);
330
331 /*
332 * Find the current position and check if it's within the volume.
333 * Writing beyond the end of a volume is not supported.
334 */
335 uint64_t offUnsigned = off < 0 ? pThis->offCurPos : (uint64_t)off;
336 uint64_t const cbImage = VDGetSize(pThis->pDisk, VD_LAST_IMAGE);
337 if (offUnsigned >= cbImage)
338 {
339 if (pcbWritten)
340 {
341 *pcbWritten = 0;
342 pThis->offCurPos = cbImage;
343 }
344 return VERR_EOF;
345 }
346
347 size_t cbLeftToWrite;
348 if (offUnsigned + pSgBuf->paSegs[0].cbSeg < cbImage)
349 {
350 cbLeftToWrite = pSgBuf->paSegs[0].cbSeg;
351 if (pcbWritten)
352 *pcbWritten = cbLeftToWrite;
353 }
354 else
355 {
356 if (!pcbWritten)
357 return VERR_EOF;
358 *pcbWritten = cbLeftToWrite = (size_t)(cbImage - offUnsigned);
359 }
360
361 /*
362 * Ok, we've got a valid stretch within the file. Do the reading.
363 */
364 int rc = VINF_SUCCESS;
365 if (cbLeftToWrite > 0)
366 {
367 rc = vdWriteHelper(pThis->pDisk, offUnsigned, pSgBuf->paSegs[0].pvSeg, cbLeftToWrite);
368 if (RT_SUCCESS(rc))
369 offUnsigned += cbLeftToWrite;
370 }
371
372 pThis->offCurPos = offUnsigned;
373 return rc;
374}
375
376
377/**
378 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnFlush}
379 */
380static DECLCALLBACK(int) vdVfsFile_Flush(void *pvThis)
381{
382 PVDVFSFILE pThis = (PVDVFSFILE)pvThis;
383 return VDFlush(pThis->pDisk);
384}
385
386
387/**
388 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnPollOne}
389 */
390static DECLCALLBACK(int) vdVfsFile_PollOne(void *pvThis, uint32_t fEvents, RTMSINTERVAL cMillies, bool fIntr,
391 uint32_t *pfRetEvents)
392{
393 NOREF(pvThis);
394 int rc;
395 if (fEvents != RTPOLL_EVT_ERROR)
396 {
397 *pfRetEvents = fEvents & ~RTPOLL_EVT_ERROR;
398 rc = VINF_SUCCESS;
399 }
400 else
401 rc = RTVfsUtilDummyPollOne(fEvents, cMillies, fIntr, pfRetEvents);
402 return rc;
403}
404
405
406/**
407 * @interface_method_impl{RTVFSIOSTREAMOPS,pfnTell}
408 */
409static DECLCALLBACK(int) vdVfsFile_Tell(void *pvThis, PRTFOFF poffActual)
410{
411 PVDVFSFILE pThis = (PVDVFSFILE)pvThis;
412 *poffActual = pThis->offCurPos;
413 return VINF_SUCCESS;
414}
415
416
417/**
418 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetMode}
419 */
420static DECLCALLBACK(int) vdVfsFile_SetMode(void *pvThis, RTFMODE fMode, RTFMODE fMask)
421{
422 NOREF(pvThis);
423 NOREF(fMode);
424 NOREF(fMask);
425 return VERR_NOT_SUPPORTED;
426}
427
428
429/**
430 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetTimes}
431 */
432static DECLCALLBACK(int) vdVfsFile_SetTimes(void *pvThis, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
433 PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
434{
435 NOREF(pvThis);
436 NOREF(pAccessTime);
437 NOREF(pModificationTime);
438 NOREF(pChangeTime);
439 NOREF(pBirthTime);
440 return VERR_NOT_SUPPORTED;
441}
442
443
444/**
445 * @interface_method_impl{RTVFSOBJSETOPS,pfnSetOwner}
446 */
447static DECLCALLBACK(int) vdVfsFile_SetOwner(void *pvThis, RTUID uid, RTGID gid)
448{
449 NOREF(pvThis);
450 NOREF(uid);
451 NOREF(gid);
452 return VERR_NOT_SUPPORTED;
453}
454
455
456/**
457 * @interface_method_impl{RTVFSFILEOPS,pfnSeek}
458 */
459static DECLCALLBACK(int) vdVfsFile_Seek(void *pvThis, RTFOFF offSeek, unsigned uMethod, PRTFOFF poffActual)
460{
461 PVDVFSFILE pThis = (PVDVFSFILE)pvThis;
462
463 /*
464 * Seek relative to which position.
465 */
466 uint64_t offWrt;
467 switch (uMethod)
468 {
469 case RTFILE_SEEK_BEGIN:
470 offWrt = 0;
471 break;
472
473 case RTFILE_SEEK_CURRENT:
474 offWrt = pThis->offCurPos;
475 break;
476
477 case RTFILE_SEEK_END:
478 offWrt = VDGetSize(pThis->pDisk, VD_LAST_IMAGE);
479 break;
480
481 default:
482 return VERR_INTERNAL_ERROR_5;
483 }
484
485 /*
486 * Calc new position, take care to stay without bounds.
487 */
488 uint64_t offNew;
489 if (offSeek == 0)
490 offNew = offWrt;
491 else if (offSeek > 0)
492 {
493 offNew = offWrt + offSeek;
494 if ( offNew < offWrt
495 || offNew > RTFOFF_MAX)
496 offNew = RTFOFF_MAX;
497 }
498 else if ((uint64_t)-offSeek < offWrt)
499 offNew = offWrt + offSeek;
500 else
501 offNew = 0;
502
503 /*
504 * Update the state and set return value.
505 */
506 pThis->offCurPos = offNew;
507
508 *poffActual = offNew;
509 return VINF_SUCCESS;
510}
511
512
513/**
514 * @interface_method_impl{RTVFSFILEOPS,pfnQuerySize}
515 */
516static DECLCALLBACK(int) vdVfsFile_QuerySize(void *pvThis, uint64_t *pcbFile)
517{
518 PVDVFSFILE pThis = (PVDVFSFILE)pvThis;
519 *pcbFile = VDGetSize(pThis->pDisk, VD_LAST_IMAGE);
520 return VINF_SUCCESS;
521}
522
523
524/**
525 * Standard file operations.
526 */
527DECL_HIDDEN_CONST(const RTVFSFILEOPS) g_vdVfsStdFileOps =
528{
529 { /* Stream */
530 { /* Obj */
531 RTVFSOBJOPS_VERSION,
532 RTVFSOBJTYPE_FILE,
533 "VDFile",
534 vdVfsFile_Close,
535 vdVfsFile_QueryInfo,
536 RTVFSOBJOPS_VERSION
537 },
538 RTVFSIOSTREAMOPS_VERSION,
539 RTVFSIOSTREAMOPS_FEAT_NO_SG,
540 vdVfsFile_Read,
541 vdVfsFile_Write,
542 vdVfsFile_Flush,
543 vdVfsFile_PollOne,
544 vdVfsFile_Tell,
545 NULL /*Skip*/,
546 NULL /*ZeroFill*/,
547 RTVFSIOSTREAMOPS_VERSION,
548 },
549 RTVFSFILEOPS_VERSION,
550 /*RTVFSIOFILEOPS_FEAT_NO_AT_OFFSET*/ 0,
551 { /* ObjSet */
552 RTVFSOBJSETOPS_VERSION,
553 RT_OFFSETOF(RTVFSFILEOPS, Stream.Obj) - RT_OFFSETOF(RTVFSFILEOPS, ObjSet),
554 vdVfsFile_SetMode,
555 vdVfsFile_SetTimes,
556 vdVfsFile_SetOwner,
557 RTVFSOBJSETOPS_VERSION
558 },
559 vdVfsFile_Seek,
560 vdVfsFile_QuerySize,
561 RTVFSFILEOPS_VERSION
562};
563
564
565VBOXDDU_DECL(int) VDCreateVfsFileFromDisk(PVDISK pDisk, uint32_t fFlags,
566 PRTVFSFILE phVfsFile)
567{
568 AssertPtrReturn(pDisk, VERR_INVALID_HANDLE);
569 AssertPtrReturn(phVfsFile, VERR_INVALID_POINTER);
570 AssertReturn((fFlags & ~VD_VFSFILE_FLAGS_MASK) == 0, VERR_INVALID_PARAMETER);
571
572 /*
573 * Create the volume file.
574 */
575 RTVFSFILE hVfsFile;
576 PVDVFSFILE pThis;
577 int rc = RTVfsNewFile(&g_vdVfsStdFileOps, sizeof(*pThis), RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_WRITE,
578 NIL_RTVFS, NIL_RTVFSLOCK, &hVfsFile, (void **)&pThis);
579 if (RT_SUCCESS(rc))
580 {
581 pThis->offCurPos = 0;
582 pThis->pDisk = pDisk;
583 pThis->fFlags = fFlags;
584
585 *phVfsFile = hVfsFile;
586 return VINF_SUCCESS;
587 }
588
589 return rc;
590}
591
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