VirtualBox

source: vbox/trunk/src/VBox/Devices/Storage/DrvDiskIntegrity.cpp@ 27806

Last change on this file since 27806 was 27561, checked in by vboxsync, 15 years ago

DrvDiskIntegrity: Save the last write access to sectors. Makes searching in huge logs a lot easier

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 15.5 KB
Line 
1/* $Id: DrvDiskIntegrity.cpp 27561 2010-03-21 13:59:33Z vboxsync $ */
2/** @file
3 * VBox storage devices: Disk integrity check.
4 */
5
6/*
7 * Copyright (C) 2006-2010 Sun Microsystems, Inc.
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 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22
23/*******************************************************************************
24* Header Files *
25*******************************************************************************/
26#define LOG_GROUP LOG_GROUP_DRV_DISK_INTEGRITY
27#include <VBox/pdmdrv.h>
28#include <iprt/assert.h>
29#include <iprt/string.h>
30#include <iprt/uuid.h>
31#include <iprt/avl.h>
32#include <iprt/mem.h>
33#include <iprt/message.h>
34
35#include "Builtins.h"
36
37
38/*******************************************************************************
39* Structures and Typedefs *
40*******************************************************************************/
41
42/**
43 * I/O log entry.
44 */
45typedef struct IOLOGENT
46{
47 /** Start offset */
48 uint64_t off;
49 /** Write size */
50 size_t cbWrite;
51 /** Number of references to this entry. */
52 unsigned cRefs;
53} IOLOGENT, *PIOLOGENT;
54
55/**
56 * Disk segment.
57 */
58typedef struct DRVDISKSEGMENT
59{
60 /** AVL core. */
61 AVLRFOFFNODECORE Core;
62 /** Size of the segment */
63 size_t cbSeg;
64 /** Data for this segment */
65 uint8_t *pbSeg;
66 /** Numbner of entries in the I/O array. */
67 unsigned cIoLogEntries;
68 /** Array of I/O log references. */
69 PIOLOGENT apIoLog[1];
70} DRVDISKSEGMENT, *PDRVDISKSEGMENT;
71
72/**
73 * Disk integrity driver instance data.
74 *
75 * @implements PDMIMEDIA
76 */
77typedef struct DRVDISKINTEGRITY
78{
79 /** Pointer driver instance. */
80 PPDMDRVINS pDrvIns;
81 /** Pointer to the media driver below us.
82 * This is NULL if the media is not mounted. */
83 PPDMIMEDIA pDrvMedia;
84 /** Our media interface */
85 PDMIMEDIA IMedia;
86
87 /** AVL tree containing the disk blocks to check. */
88 PAVLRFOFFTREE pTreeSegments;
89} DRVDISKINTEGRITY, *PDRVDISKINTEGRITY;
90
91
92/* -=-=-=-=- IMedia -=-=-=-=- */
93
94/** Makes a PDRVDISKINTEGRITY out of a PPDMIMEDIA. */
95#define PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface) ( (PDRVDISKINTEGRITY)((uintptr_t)pInterface - RT_OFFSETOF(DRVDISKINTEGRITY, IMedia)) )
96
97/*******************************************************************************
98* Media interface methods *
99*******************************************************************************/
100
101/** @copydoc PDMIMEDIA::pfnRead */
102static DECLCALLBACK(int) drvdiskintRead(PPDMIMEDIA pInterface,
103 uint64_t off, void *pvBuf, size_t cbRead)
104{
105 PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
106 int rc = pThis->pDrvMedia->pfnRead(pThis->pDrvMedia, off, pvBuf, cbRead);
107 if (RT_FAILURE(rc))
108 return rc;
109
110 Assert(off % 512 == 0);
111 Assert(cbRead % 512 == 0);
112
113 /* Compare read data */
114 size_t cbLeft = cbRead;
115 RTFOFF offCurr = (RTFOFF)off;
116 uint8_t *pbBuf = (uint8_t *)pvBuf;
117
118 while (cbLeft)
119 {
120 PDRVDISKSEGMENT pSeg = (PDRVDISKSEGMENT)RTAvlrFileOffsetRangeGet(pThis->pTreeSegments, offCurr);
121 size_t cbRange = 0;
122 bool fCmp = false;
123 unsigned offSeg = 0;
124
125 if (!pSeg)
126 {
127 /* Get next segment */
128 pSeg = (PDRVDISKSEGMENT)RTAvlrFileOffsetGetBestFit(pThis->pTreeSegments, offCurr, true);
129 if (!pSeg)
130 {
131 /* No data in the tree for this read. Assume everything is ok. */
132 cbRange = cbLeft;
133 }
134 else if (offCurr + (RTFOFF)cbLeft <= pSeg->Core.Key)
135 cbRange = cbLeft;
136 else
137 cbRange = pSeg->Core.Key - offCurr;
138 }
139 else
140 {
141 fCmp = true;
142 offSeg = offCurr - pSeg->Core.Key;
143 cbRange = RT_MIN(cbLeft, (size_t)(pSeg->Core.KeyLast + 1 - offCurr));
144 }
145
146 if ( fCmp
147 && memcmp(pbBuf, pSeg->pbSeg + offSeg, cbRange))
148 {
149 unsigned offWrong = 0;
150 for (offWrong = 0; offWrong < cbRange; offWrong++)
151 if (pbBuf[offWrong] != pSeg->pbSeg[offSeg + offWrong])
152 {
153 /* Corrupted disk, print I/O log entry of the last write which accessed this range. */
154 uint32_t cSector = (offSeg + offWrong) / 512;
155 AssertMsg(cSector < pSeg->cIoLogEntries, ("Internal bug!\n"));
156
157 RTMsgError("Corrupted disk at offset %llu (%u bytes in the current read buffer)!\n",
158 offCurr + offWrong, offWrong);
159 RTMsgError("Last write to this sector started at offset %llu with %u bytes (%u references to this log entry)\n",
160 pSeg->apIoLog[cSector]->off,
161 pSeg->apIoLog[cSector]->cbWrite,
162 pSeg->apIoLog[cSector]->cRefs);
163 RTAssertDebugBreak();
164 }
165 }
166
167 offCurr += cbRange;
168 cbLeft -= cbRange;
169 pbBuf += cbRange;
170 }
171
172 return rc;
173}
174
175/** @copydoc PDMIMEDIA::pfnWrite */
176static DECLCALLBACK(int) drvdiskintWrite(PPDMIMEDIA pInterface,
177 uint64_t off, const void *pvBuf,
178 size_t cbWrite)
179{
180 PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
181 int rc = pThis->pDrvMedia->pfnWrite(pThis->pDrvMedia, off, pvBuf, cbWrite);
182 if (RT_FAILURE(rc))
183 return rc;
184
185 /* Update the segments */
186 size_t cbLeft = cbWrite;
187 RTFOFF offCurr = (RTFOFF)off;
188 uint8_t *pbBuf = (uint8_t *)pvBuf;
189 PIOLOGENT pIoLogEnt = (PIOLOGENT)RTMemAllocZ(sizeof(IOLOGENT));
190 if (!pIoLogEnt)
191 return VERR_NO_MEMORY;
192
193 pIoLogEnt->off = off;
194 pIoLogEnt->cbWrite = cbWrite;
195 pIoLogEnt->cRefs = 0;
196
197 while (cbLeft)
198 {
199 PDRVDISKSEGMENT pSeg = (PDRVDISKSEGMENT)RTAvlrFileOffsetRangeGet(pThis->pTreeSegments, offCurr);
200 size_t cbRange = 0;
201 bool fSet = false;
202 unsigned offSeg = 0;
203
204 if (!pSeg)
205 {
206 /* Get next segment */
207 pSeg = (PDRVDISKSEGMENT)RTAvlrFileOffsetGetBestFit(pThis->pTreeSegments, offCurr, true);
208 if ( !pSeg
209 || offCurr + (RTFOFF)cbLeft <= pSeg->Core.Key)
210 cbRange = cbLeft;
211 else
212 cbRange = pSeg->Core.Key - offCurr;
213
214 Assert(cbRange % 512 == 0);
215
216 /* Create new segment */
217 pSeg = (PDRVDISKSEGMENT)RTMemAllocZ(RT_OFFSETOF(DRVDISKSEGMENT, apIoLog[cbRange / 512]));
218 if (pSeg)
219 {
220 pSeg->Core.Key = offCurr;
221 pSeg->Core.KeyLast = offCurr + (RTFOFF)cbRange - 1;
222 pSeg->cbSeg = cbRange;
223 pSeg->pbSeg = (uint8_t *)RTMemAllocZ(cbRange);
224 pSeg->cIoLogEntries = cbRange / 512;
225 if (!pSeg->pbSeg)
226 RTMemFree(pSeg);
227 else
228 {
229 bool fInserted = RTAvlrFileOffsetInsert(pThis->pTreeSegments, &pSeg->Core);
230 AssertMsg(fInserted, ("Bug!\n"));
231 fSet = true;
232 }
233 }
234 }
235 else
236 {
237 fSet = true;
238 offSeg = offCurr - pSeg->Core.Key;
239 cbRange = RT_MIN(cbLeft, (size_t)(pSeg->Core.KeyLast + 1 - offCurr));
240 }
241
242 if (fSet)
243 {
244 AssertPtr(pSeg);
245 memcpy(pSeg->pbSeg + offSeg, pbBuf, cbRange);
246
247 /* Update the I/O log pointers */
248 Assert(offSeg % 512 == 0);
249 Assert(cbRange % 512 == 0);
250 while (offSeg < cbRange)
251 {
252 uint32_t uSector = offSeg / 512;
253 PIOLOGENT pIoLogOld = NULL;
254
255 AssertMsg(uSector < pSeg->cIoLogEntries, ("Internal bug!\n"));
256
257 pIoLogOld = pSeg->apIoLog[uSector];
258 if (pIoLogOld)
259 {
260 pIoLogOld->cRefs--;
261 if (!pIoLogOld->cRefs)
262 RTMemFree(pIoLogOld);
263 }
264
265 pSeg->apIoLog[uSector] = pIoLogEnt;
266 pIoLogEnt->cRefs++;
267
268 offSeg += 512;
269 }
270 }
271
272 offCurr += cbRange;
273 cbLeft -= cbRange;
274 pbBuf += cbRange;
275 }
276
277 return rc;
278}
279
280/** @copydoc PDMIMEDIA::pfnFlush */
281static DECLCALLBACK(int) drvdiskintFlush(PPDMIMEDIA pInterface)
282{
283 PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
284 return pThis->pDrvMedia->pfnFlush(pThis->pDrvMedia);
285}
286
287/** @copydoc PDMIMEDIA::pfnGetSize */
288static DECLCALLBACK(uint64_t) drvdiskintGetSize(PPDMIMEDIA pInterface)
289{
290 PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
291 return pThis->pDrvMedia->pfnGetSize(pThis->pDrvMedia);
292}
293
294/** @copydoc PDMIMEDIA::pfnIsReadOnly */
295static DECLCALLBACK(bool) drvdiskintIsReadOnly(PPDMIMEDIA pInterface)
296{
297 PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
298 return pThis->pDrvMedia->pfnIsReadOnly(pThis->pDrvMedia);
299}
300
301/** @copydoc PDMIMEDIA::pfnBiosGetPCHSGeometry */
302static DECLCALLBACK(int) drvdiskintBiosGetPCHSGeometry(PPDMIMEDIA pInterface,
303 PPDMMEDIAGEOMETRY pPCHSGeometry)
304{
305 PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
306 return pThis->pDrvMedia->pfnBiosGetPCHSGeometry(pThis->pDrvMedia, pPCHSGeometry);
307}
308
309/** @copydoc PDMIMEDIA::pfnBiosSetPCHSGeometry */
310static DECLCALLBACK(int) drvdiskintBiosSetPCHSGeometry(PPDMIMEDIA pInterface,
311 PCPDMMEDIAGEOMETRY pPCHSGeometry)
312{
313 PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
314 return pThis->pDrvMedia->pfnBiosSetPCHSGeometry(pThis->pDrvMedia, pPCHSGeometry);
315}
316
317/** @copydoc PDMIMEDIA::pfnBiosGetLCHSGeometry */
318static DECLCALLBACK(int) drvdiskintBiosGetLCHSGeometry(PPDMIMEDIA pInterface,
319 PPDMMEDIAGEOMETRY pLCHSGeometry)
320{
321 PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
322 return pThis->pDrvMedia->pfnBiosGetLCHSGeometry(pThis->pDrvMedia, pLCHSGeometry);
323}
324
325/** @copydoc PDMIMEDIA::pfnBiosSetLCHSGeometry */
326static DECLCALLBACK(int) drvdiskintBiosSetLCHSGeometry(PPDMIMEDIA pInterface,
327 PCPDMMEDIAGEOMETRY pLCHSGeometry)
328{
329 PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
330 return pThis->pDrvMedia->pfnBiosSetLCHSGeometry(pThis->pDrvMedia, pLCHSGeometry);
331}
332
333/** @copydoc PDMIMEDIA::pfnGetUuid */
334static DECLCALLBACK(int) drvdiskintGetUuid(PPDMIMEDIA pInterface, PRTUUID pUuid)
335{
336 PDRVDISKINTEGRITY pThis = PDMIMEDIA_2_DRVDISKINTEGRITY(pInterface);
337 return pThis->pDrvMedia->pfnGetUuid(pThis->pDrvMedia, pUuid);
338}
339
340/* -=-=-=-=- IBase -=-=-=-=- */
341
342/**
343 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
344 */
345static DECLCALLBACK(void *) drvdiskintQueryInterface(PPDMIBASE pInterface, const char *pszIID)
346{
347 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
348 PDRVDISKINTEGRITY pThis = PDMINS_2_DATA(pDrvIns, PDRVDISKINTEGRITY);
349
350 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
351 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIA, &pThis->IMedia);
352 return NULL;
353}
354
355
356/* -=-=-=-=- driver interface -=-=-=-=- */
357
358static int drvdiskintTreeDestroy(PAVLRFOFFNODECORE pNode, void *pvUser)
359{
360 PDRVDISKSEGMENT pSeg = (PDRVDISKSEGMENT)pNode;
361
362 RTMemFree(pSeg->pbSeg);
363 RTMemFree(pSeg);
364 return VINF_SUCCESS;
365}
366
367/**
368 * @copydoc FNPDMDRVDESTRUCT
369 */
370static DECLCALLBACK(void) drvdiskintDestruct(PPDMDRVINS pDrvIns)
371{
372 PDRVDISKINTEGRITY pThis = PDMINS_2_DATA(pDrvIns, PDRVDISKINTEGRITY);
373
374 if (pThis->pTreeSegments)
375 {
376 RTAvlrFileOffsetDestroy(pThis->pTreeSegments, drvdiskintTreeDestroy, NULL);
377 RTMemFree(pThis->pTreeSegments);
378 }
379}
380
381/**
382 * Construct a disk integrity driver instance.
383 *
384 * @copydoc FNPDMDRVCONSTRUCT
385 */
386static DECLCALLBACK(int) drvdiskintConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
387{
388 PDRVDISKINTEGRITY pThis = PDMINS_2_DATA(pDrvIns, PDRVDISKINTEGRITY);
389 LogFlow(("drvdiskintConstruct: iInstance=%d\n", pDrvIns->iInstance));
390 PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
391
392 /*
393 * Validate configuration.
394 */
395 if (!CFGMR3AreValuesValid(pCfg, ""))
396 return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES;
397
398 /*
399 * Initialize most of the data members.
400 */
401 pThis->pDrvIns = pDrvIns;
402
403 /* IBase. */
404 pDrvIns->IBase.pfnQueryInterface = drvdiskintQueryInterface;
405
406 /* IMedia */
407 pThis->IMedia.pfnRead = drvdiskintRead;
408 pThis->IMedia.pfnWrite = drvdiskintWrite;
409 pThis->IMedia.pfnFlush = drvdiskintFlush;
410 pThis->IMedia.pfnGetSize = drvdiskintGetSize;
411 pThis->IMedia.pfnIsReadOnly = drvdiskintIsReadOnly;
412 pThis->IMedia.pfnBiosGetPCHSGeometry = drvdiskintBiosGetPCHSGeometry;
413 pThis->IMedia.pfnBiosSetPCHSGeometry = drvdiskintBiosSetPCHSGeometry;
414 pThis->IMedia.pfnBiosGetLCHSGeometry = drvdiskintBiosGetLCHSGeometry;
415 pThis->IMedia.pfnBiosSetLCHSGeometry = drvdiskintBiosSetLCHSGeometry;
416 pThis->IMedia.pfnGetUuid = drvdiskintGetUuid;
417
418 /*
419 * Try attach driver below and query it's media interface.
420 */
421 PPDMIBASE pBase;
422 int rc = PDMDrvHlpAttach(pDrvIns, fFlags, &pBase);
423 if (RT_FAILURE(rc))
424 return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
425 N_("Failed to attach driver below us! %Rrf"), rc);
426
427 pThis->pDrvMedia = PDMIBASE_QUERY_INTERFACE(pBase, PDMIMEDIA);
428 if (!pThis->pDrvMedia)
429 return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_BELOW,
430 N_("No media or async media interface below"));
431
432 /** Create the AVL tree. */
433 pThis->pTreeSegments = (PAVLRFOFFTREE)RTMemAllocZ(sizeof(AVLRFOFFTREE));
434 if (!pThis->pTreeSegments)
435 rc = VERR_NO_MEMORY;
436
437 return rc;
438}
439
440
441/**
442 * Block driver registration record.
443 */
444const PDMDRVREG g_DrvDiskIntegrity =
445{
446 /* u32Version */
447 PDM_DRVREG_VERSION,
448 /* szName */
449 "DiskIntegrity",
450 /* szRCMod */
451 "",
452 /* szR0Mod */
453 "",
454 /* pszDescription */
455 "Disk integrity driver.",
456 /* fFlags */
457 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
458 /* fClass. */
459 PDM_DRVREG_CLASS_BLOCK,
460 /* cMaxInstances */
461 ~0,
462 /* cbInstance */
463 sizeof(DRVDISKINTEGRITY),
464 /* pfnConstruct */
465 drvdiskintConstruct,
466 /* pfnDestruct */
467 drvdiskintDestruct,
468 /* pfnRelocate */
469 NULL,
470 /* pfnIOCtl */
471 NULL,
472 /* pfnPowerOn */
473 NULL,
474 /* pfnReset */
475 NULL,
476 /* pfnSuspend */
477 NULL,
478 /* pfnResume */
479 NULL,
480 /* pfnAttach */
481 NULL,
482 /* pfnDetach */
483 NULL,
484 /* pfnPowerOff */
485 NULL,
486 /* pfnSoftReset */
487 NULL,
488 /* u32EndVersion */
489 PDM_DRVREG_VERSION
490};
491
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette