/** * @interface_method_impl{RTVFSIOSTREAMOPS,pfnWrite} */ static DECLCALLBACK(int) vdVfsFile_Write(void *pvThis, RTFOFF off, PCRTSGBUF pSgBuf, bool fBlocking, size_t *pcbWritten) { PVDVFSFILE pThis = (PVDVFSFILE)pvThis; int rc = VINF_SUCCESS; Assert(pSgBuf->cSegs == 1); NOREF(fBlocking); /* * Find the current position and check if it's within the volume. * Writing beyond the end of a volume is not supported. */ uint64_t offUnsigned = off < 0 ? pThis->offCurPos : (uint64_t)off; if (offUnsigned >= VDGetSize(pThis->pDisk, VD_LAST_IMAGE)) { if (pcbWritten) { *pcbWritten = 0; pThis->offCurPos = offUnsigned; } return VERR_NOT_SUPPORTED; } size_t cbLeftToWrite; if (offUnsigned + pSgBuf->paSegs[0].cbSeg > VDGetSize(pThis->pDisk, VD_LAST_IMAGE)) { if (!pcbWritten) return VERR_EOF; *pcbWritten = cbLeftToWrite = (size_t)(VDGetSize(pThis->pDisk, VD_LAST_IMAGE) - offUnsigned); } else { cbLeftToWrite = pSgBuf->paSegs[0].cbSeg; if (pcbWritten) *pcbWritten = cbLeftToWrite; } /* * Ok, we've got a valid stretch within the file. Do the reading. */ if (cbLeftToWrite > 0) { rc = vdWriteHelper(pThis->pDisk, (uint64_t)off, pSgBuf->paSegs[0].pvSeg, cbLeftToWrite); if (RT_SUCCESS(rc)) offUnsigned += cbLeftToWrite; } pThis->offCurPos = offUnsigned; return rc; }
/** * @interface_method_impl{RTVFSFILEOPS,pfnSeek} */ static DECLCALLBACK(int) vdVfsFile_Seek(void *pvThis, RTFOFF offSeek, unsigned uMethod, PRTFOFF poffActual) { PVDVFSFILE pThis = (PVDVFSFILE)pvThis; /* * Seek relative to which position. */ uint64_t offWrt; switch (uMethod) { case RTFILE_SEEK_BEGIN: offWrt = 0; break; case RTFILE_SEEK_CURRENT: offWrt = pThis->offCurPos; break; case RTFILE_SEEK_END: offWrt = VDGetSize(pThis->pDisk, VD_LAST_IMAGE); break; default: return VERR_INTERNAL_ERROR_5; } /* * Calc new position, take care to stay within bounds. * * @todo: Setting position beyond the end of the disk does not make sense. */ uint64_t offNew; if (offSeek == 0) offNew = offWrt; else if (offSeek > 0) { offNew = offWrt + offSeek; if ( offNew < offWrt || offNew > RTFOFF_MAX) offNew = RTFOFF_MAX; } else if ((uint64_t)-offSeek < offWrt) offNew = offWrt + offSeek; else offNew = 0; /* * Update the state and set return value. */ pThis->offCurPos = offNew; *poffActual = offNew; return VINF_SUCCESS; }
/** * Creates a flattened image * * @returns VBox status code. * @param pszPath Where to create the flattened file in the FUSE file * system. * @param pszImage The image to flatten. * @param ppFile Where to return the pointer to the instance. * Optional. */ static int vboxfuseFlatImageCreate(const char *pszPath, const char *pszImage, PVBOXFUSEFLATIMAGE *ppFile) { /* * Check that we can create this file. */ const char *pszName; PVBOXFUSEDIR pParent; int rc = vboxfuseTreeLookupParentForInsert(pszPath, &pszName, &pParent); if (RT_FAILURE(rc)) return rc; if (pParent) vboxfuseNodeReleaseAndUnlock(&pParent->Node); /* * Try open the image file (without holding any locks). */ char *pszFormat; VDTYPE enmType; rc = VDGetFormat(NULL /* pVDIIfsDisk */, NULL /* pVDIIfsImage*/, pszImage, &pszFormat, &enmType); if (RT_FAILURE(rc)) { LogRel(("VDGetFormat(%s,) failed, rc=%Rrc\n", pszImage, rc)); return rc; } PVBOXHDD pDisk = NULL; rc = VDCreate(NULL /* pVDIIfsDisk */, enmType, &pDisk); if (RT_SUCCESS(rc)) { rc = VDOpen(pDisk, pszFormat, pszImage, 0, NULL /* pVDIfsImage */); if (RT_FAILURE(rc)) { LogRel(("VDCreate(,%s,%s,,,) failed, rc=%Rrc\n", pszFormat, pszImage, rc)); VDClose(pDisk, false /* fDeletes */); } } else Log(("VDCreate failed, rc=%Rrc\n", rc)); if (RT_FAILURE(rc)) { RTStrFree(pszFormat); return rc; } /* * Allocate and initialize the new directory node. */ rc = vboxfuseTreeLookupParentForInsert(pszPath, &pszName, &pParent); if (RT_SUCCESS(rc)) { PVBOXFUSEFLATIMAGE pNewFlatImage; rc = vboxfuseNodeAlloc(sizeof(*pNewFlatImage), pszName, VBOXFUSETYPE_FLAT_IMAGE, pParent, (PVBOXFUSENODE *)&pNewFlatImage); if (RT_SUCCESS(rc)) { pNewFlatImage->pDisk = pDisk; pNewFlatImage->pszFormat = pszFormat; pNewFlatImage->cReaders = VDIsReadOnly(pNewFlatImage->pDisk) ? INT32_MAX / 2 : 0; pNewFlatImage->cWriters = 0; pNewFlatImage->Node.cbPrimary = VDGetSize(pNewFlatImage->pDisk, 0 /* base */); /* * Insert it. */ rc = vboxfuseDirInsertChild(pParent, &pNewFlatImage->Node); if ( RT_SUCCESS(rc) && ppFile) { vboxfuseNodeLockAndRetain(&pNewFlatImage->Node); *ppFile = pNewFlatImage; } vboxfuseNodeRelease(&pNewFlatImage->Node); pDisk = NULL; } if (pParent) vboxfuseNodeReleaseAndUnlock(&pParent->Node); } if (RT_FAILURE(rc) && pDisk != NULL) VDClose(pDisk, false /* fDelete */); return rc; }
/** * @interface_method_impl{RTVFSFILEOPS,pfnQuerySize} */ static DECLCALLBACK(int) vdVfsFile_QuerySize(void *pvThis, uint64_t *pcbFile) { PVDVFSFILE pThis = (PVDVFSFILE)pvThis; *pcbFile = VDGetSize(pThis->pDisk, VD_LAST_IMAGE); return VINF_SUCCESS; }
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; }