/** @copydoc fuse_operations::write */ static int vboxfuseOp_write(const char *pszPath, const char *pbBuf, size_t cbBuf, off_t offFile, struct fuse_file_info *pInfo) { /* paranoia */ AssertReturn((int)cbBuf >= 0, -EINVAL); AssertReturn((unsigned)cbBuf == cbBuf, -EINVAL); AssertReturn(offFile >= 0, -EINVAL); AssertReturn((off_t)(offFile + cbBuf) >= offFile, -EINVAL); PVBOXFUSENODE pNode = (PVBOXFUSENODE)(uintptr_t)pInfo->fh; AssertPtr(pNode); switch (pNode->enmType) { case VBOXFUSETYPE_DIRECTORY: return -ENOTSUP; case VBOXFUSETYPE_FLAT_IMAGE: { PVBOXFUSEFLATIMAGE pFlatImage = (PVBOXFUSEFLATIMAGE)(uintptr_t)pInfo->fh; LogFlow(("vboxfuseOp_write: offFile=%#llx cbBuf=%#zx pszPath=\"%s\"\n", (uint64_t)offFile, cbBuf, pszPath)); vboxfuseNodeLock(&pFlatImage->Node); int rc; if ((off_t)(offFile + cbBuf) < offFile) rc = -EINVAL; else if (offFile >= pFlatImage->Node.cbPrimary) rc = 0; else if (!cbBuf) rc = 0; else { /* Adjust for EOF. */ if ((off_t)(offFile + cbBuf) >= pFlatImage->Node.cbPrimary) cbBuf = pFlatImage->Node.cbPrimary - offFile; /* * Aligned write? */ int rc2; if ( !(offFile & VBOXFUSE_MIN_SIZE_MASK_OFF) && !(cbBuf & VBOXFUSE_MIN_SIZE_MASK_OFF)) rc2 = VDWrite(pFlatImage->pDisk, offFile, pbBuf, cbBuf); else { /* * Unaligned write - lots of extra work. */ uint8_t abBlock[VBOXFUSE_MIN_SIZE]; if (((offFile + cbBuf) & VBOXFUSE_MIN_SIZE_MASK_BLK) == (offFile & VBOXFUSE_MIN_SIZE_MASK_BLK)) { /* a single partial block. */ rc2 = VDRead(pFlatImage->pDisk, offFile & VBOXFUSE_MIN_SIZE_MASK_BLK, abBlock, VBOXFUSE_MIN_SIZE); if (RT_SUCCESS(rc2)) { memcpy(&abBlock[offFile & VBOXFUSE_MIN_SIZE_MASK_OFF], pbBuf, cbBuf); /* Update the block */ rc2 = VDWrite(pFlatImage->pDisk, offFile & VBOXFUSE_MIN_SIZE_MASK_BLK, abBlock, VBOXFUSE_MIN_SIZE); } } else { /* read unaligned head. */ rc2 = VINF_SUCCESS; if (offFile & VBOXFUSE_MIN_SIZE_MASK_OFF) { rc2 = VDRead(pFlatImage->pDisk, offFile & VBOXFUSE_MIN_SIZE_MASK_BLK, abBlock, VBOXFUSE_MIN_SIZE); if (RT_SUCCESS(rc2)) { size_t cbCopy = VBOXFUSE_MIN_SIZE - (offFile & VBOXFUSE_MIN_SIZE_MASK_OFF); memcpy(&abBlock[offFile & VBOXFUSE_MIN_SIZE_MASK_OFF], pbBuf, cbCopy); pbBuf += cbCopy; offFile += cbCopy; cbBuf -= cbCopy; rc2 = VDWrite(pFlatImage->pDisk, offFile & VBOXFUSE_MIN_SIZE_MASK_BLK, abBlock, VBOXFUSE_MIN_SIZE); } } /* write the middle. */ Assert(!(offFile & VBOXFUSE_MIN_SIZE_MASK_OFF)); if (cbBuf >= VBOXFUSE_MIN_SIZE && RT_SUCCESS(rc2)) { size_t cbWrite = cbBuf & VBOXFUSE_MIN_SIZE_MASK_BLK; rc2 = VDWrite(pFlatImage->pDisk, offFile, pbBuf, cbWrite); if (RT_SUCCESS(rc2)) { pbBuf += cbWrite; offFile += cbWrite; cbBuf -= cbWrite; } } /* unaligned tail write. */ Assert(cbBuf < VBOXFUSE_MIN_SIZE); Assert(!(offFile & VBOXFUSE_MIN_SIZE_MASK_OFF)); if (cbBuf && RT_SUCCESS(rc2)) { rc2 = VDRead(pFlatImage->pDisk, offFile, abBlock, VBOXFUSE_MIN_SIZE); if (RT_SUCCESS(rc2)) { memcpy(&abBlock[0], pbBuf, cbBuf); rc2 = VDWrite(pFlatImage->pDisk, offFile, abBlock, VBOXFUSE_MIN_SIZE); } } } } /* convert the return code */ if (RT_SUCCESS(rc2)) rc = cbBuf; else rc = -RTErrConvertToErrno(rc2); } vboxfuseNodeUnlock(&pFlatImage->Node); return rc; } case VBOXFUSETYPE_CONTROL_PIPE: return -ENOTSUP; default: AssertMsgFailed(("%s\n", pszPath)); return -EDOOFUS; } }
/** * VD read helper taking care of unaligned accesses. * * @return VBox status code. * @param pDisk VD disk container. * @param off Offset to start reading from. * @param pvBuf Pointer to the buffer to read into. * @param cbRead Amount of bytes to read. */ static int vdReadHelper(PVBOXHDD pDisk, uint64_t off, void *pvBuf, size_t cbRead) { int rc = VINF_SUCCESS; /* Take shortcut if possible. */ if ( off % 512 == 0 && cbRead % 512 == 0) rc = VDRead(pDisk, off, pvBuf, cbRead); else { uint8_t *pbBuf = (uint8_t *)pvBuf; uint8_t abBuf[512]; /* Unaligned access, make it aligned. */ if (off % 512 != 0) { uint64_t offAligned = off & ~(uint64_t)(512 - 1); size_t cbToCopy = 512 - (off - offAligned); rc = VDRead(pDisk, offAligned, abBuf, 512); if (RT_SUCCESS(rc)) { memcpy(pbBuf, &abBuf[off - offAligned], cbToCopy); pbBuf += cbToCopy; off += cbToCopy; cbRead -= cbToCopy; } } if ( RT_SUCCESS(rc) && (cbRead & ~(uint64_t)(512 - 1))) { size_t cbReadAligned = cbRead & ~(uint64_t)(512 - 1); Assert(!(off % 512)); rc = VDRead(pDisk, off, pbBuf, cbReadAligned); if (RT_SUCCESS(rc)) { pbBuf += cbReadAligned; off += cbReadAligned; cbRead -= cbReadAligned; } } if ( RT_SUCCESS(rc) && cbRead) { Assert(cbRead < 512); Assert(!(off % 512)); rc = VDRead(pDisk, off, abBuf, 512); if (RT_SUCCESS(rc)) memcpy(pbBuf, abBuf, cbRead); } } return rc; }
int main(int argc, char *argv[]) { int rc; RTR3InitExe(argc, &argv, 0); if (argc != 3) { RTPrintf("Usage: ./tstVDCopy <hdd1> <hdd2>\n"); return 1; } RTPrintf("tstVDCopy: TESTING...\n"); PVBOXHDD pVD1 = NULL; PVBOXHDD pVD2 = NULL; PVDINTERFACE pVDIfs = NULL; VDINTERFACEERROR VDIfError; char *pszVD1 = NULL; char *pszVD2 = NULL; char *pbBuf1 = NULL; char *pbBuf2 = NULL; VDTYPE enmTypeVD1 = VDTYPE_INVALID; VDTYPE enmTypeVD2 = VDTYPE_INVALID; #define CHECK(str) \ do \ { \ if (RT_FAILURE(rc)) \ { \ RTPrintf("%s rc=%Rrc\n", str, rc); \ if (pVD1) \ VDCloseAll(pVD1); \ if (pVD2) \ VDCloseAll(pVD2); \ return rc; \ } \ } while (0) pbBuf1 = (char *)RTMemAllocZ(VD_MERGE_BUFFER_SIZE); pbBuf2 = (char *)RTMemAllocZ(VD_MERGE_BUFFER_SIZE); /* Create error interface. */ VDIfError.pfnError = tstVDError; rc = VDInterfaceAdd(&VDIfError.Core, "tstVD_Error", VDINTERFACETYPE_ERROR, NULL, sizeof(VDINTERFACEERROR), &pVDIfs); AssertRC(rc); rc = VDGetFormat(NULL /* pVDIfsDisk */, NULL /* pVDIfsImage */, argv[1], &pszVD1, &enmTypeVD1); CHECK("VDGetFormat() hdd1"); rc = VDGetFormat(NULL /* pVDIfsDisk */, NULL /* pVDIfsImage */, argv[2], &pszVD2, &enmTypeVD2); CHECK("VDGetFormat() hdd2"); rc = VDCreate(pVDIfs, VDTYPE_HDD, &pVD1); CHECK("VDCreate() hdd1"); rc = VDCreate(pVDIfs, VDTYPE_HDD, &pVD2); CHECK("VDCreate() hdd1"); rc = VDOpen(pVD1, pszVD1, argv[1], VD_OPEN_FLAGS_NORMAL, NULL); CHECK("VDOpen() hdd1"); rc = VDOpen(pVD2, pszVD2, argv[2], VD_OPEN_FLAGS_NORMAL, NULL); CHECK("VDOpen() hdd2"); uint64_t cbSize1 = 0; uint64_t cbSize2 = 0; cbSize1 = VDGetSize(pVD1, 0); Assert(cbSize1 != 0); cbSize2 = VDGetSize(pVD1, 0); Assert(cbSize1 != 0); if (cbSize1 == cbSize2) { uint64_t uOffCurr = 0; /* Compare block by block. */ while (uOffCurr < cbSize1) { size_t cbRead = RT_MIN((cbSize1 - uOffCurr), VD_MERGE_BUFFER_SIZE); rc = VDRead(pVD1, uOffCurr, pbBuf1, cbRead); CHECK("VDRead() hdd1"); rc = VDRead(pVD2, uOffCurr, pbBuf2, cbRead); CHECK("VDRead() hdd2"); if (memcmp(pbBuf1, pbBuf2, cbRead)) { RTPrintf("tstVDCopy: Images differ uOffCurr=%llu\n", uOffCurr); /* Do byte by byte comparison. */ for (size_t i = 0; i < cbRead; i++) { if (pbBuf1[i] != pbBuf2[i]) { RTPrintf("tstVDCopy: First different byte is at offset %llu\n", uOffCurr + i); break; } } break; } uOffCurr += cbRead; } } else RTPrintf("tstVDCopy: Images have different size hdd1=%llu hdd2=%llu\n", cbSize1, cbSize2); VDClose(pVD1, false); CHECK("VDClose() hdd1"); VDClose(pVD2, false); CHECK("VDClose() hdd2"); VDDestroy(pVD1); VDDestroy(pVD2); RTMemFree(pbBuf1); RTMemFree(pbBuf2); #undef CHECK rc = VDShutdown(); if (RT_FAILURE(rc)) { RTPrintf("tstVDCopy: unloading backends failed! rc=%Rrc\n", rc); g_cErrors++; } /* * Summary */ if (!g_cErrors) RTPrintf("tstVDCopy: SUCCESS\n"); else RTPrintf("tstVDCopy: FAILURE - %d errors\n", g_cErrors); return !!g_cErrors; }
static int tstVDSnapReadVerify(PVBOXHDD pVD, PVDDISKSEG paDiskSegments, uint32_t cDiskSegments, uint64_t cbDisk) { int rc = VINF_SUCCESS; uint8_t *pbBuf = (uint8_t *)RTMemAlloc(_1M); for (uint32_t i = 0; i < cDiskSegments; i++) { size_t cbRead = paDiskSegments[i].cbSeg; uint64_t off = paDiskSegments[i].off; uint8_t *pbCmp = paDiskSegments[i].pbData; Assert(!paDiskSegments[i].pbDataDiff); while (cbRead) { size_t cbToRead = RT_MIN(cbRead, _1M); rc = VDRead(pVD, off, pbBuf, cbToRead); if (RT_FAILURE(rc)) return rc; if (pbCmp) { if (memcmp(pbCmp, pbBuf, cbToRead)) { for (unsigned iCmp = 0; iCmp < cbToRead; iCmp++) { if (pbCmp[iCmp] != pbBuf[iCmp]) { RTPrintf("Unexpected data at %llu expected %#x got %#x\n", off+iCmp, pbCmp[iCmp], pbBuf[iCmp]); break; } } return VERR_INTERNAL_ERROR; } } else { /* Verify that the block is 0 */ for (unsigned iCmp = 0; iCmp < cbToRead; iCmp++) { if (pbBuf[iCmp] != 0) { RTPrintf("Zero block contains data at %llu\n", off+iCmp); return VERR_INTERNAL_ERROR; } } } cbRead -= cbToRead; off += cbToRead; if (pbCmp) pbCmp += cbToRead; } } RTMemFree(pbBuf); return rc; }