/** * Internal. Flush image data to disk. */ static int parallelsFlushImage(PPARALLELSIMAGE pImage) { int rc = VINF_SUCCESS; if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY) return VINF_SUCCESS; if ( !(pImage->uImageFlags & VD_IMAGE_FLAGS_FIXED) && (pImage->fAllocationBitmapChanged)) { pImage->fAllocationBitmapChanged = false; /* Write the allocation bitmap to the file. */ rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, sizeof(ParallelsHeader), pImage->pAllocationBitmap, pImage->cAllocationBitmapEntries * sizeof(uint32_t), NULL); if (RT_FAILURE(rc)) return rc; } /* Flush file. */ rc = vdIfIoIntFileFlushSync(pImage->pIfIo, pImage->pStorage); LogFlowFunc(("returns %Rrc\n", rc)); return rc; }
/** * Internal. Free all allocated space for representing an image except pImage, * and optionally delete the image from disk. */ static int rawFreeImage(PRAWIMAGE pImage, bool fDelete) { int rc = VINF_SUCCESS; /* Freeing a never allocated image (e.g. because the open failed) is * not signalled as an error. After all nothing bad happens. */ if (pImage) { if (pImage->pStorage) { /* No point updating the file that is deleted anyway. */ if (!fDelete) { /* For newly created images in sequential mode fill it to * the nominal size. */ if ( pImage->uOpenFlags & VD_OPEN_FLAGS_SEQUENTIAL && !(pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY) && pImage->fCreate) { /* Fill rest of image with zeroes, a must for sequential * images to reach the nominal size. */ uint64_t uOff; void *pvBuf = RTMemTmpAllocZ(RAW_FILL_SIZE); if (RT_LIKELY(pvBuf)) { uOff = pImage->offAccess; /* Write data to all image blocks. */ while (uOff < pImage->cbSize) { unsigned cbChunk = (unsigned)RT_MIN(pImage->cbSize - uOff, RAW_FILL_SIZE); rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, uOff, pvBuf, cbChunk); if (RT_FAILURE(rc)) break; uOff += cbChunk; } RTMemTmpFree(pvBuf); } else rc = VERR_NO_MEMORY; } rawFlushImage(pImage); } rc = vdIfIoIntFileClose(pImage->pIfIo, pImage->pStorage); pImage->pStorage = NULL; } if (fDelete && pImage->pszFilename) vdIfIoIntFileDelete(pImage->pIfIo, pImage->pszFilename); } LogFlowFunc(("returns %Rrc\n", rc)); return rc; }
/** @copydoc VBOXHDDBACKEND::pfnWrite */ static int rawWrite(void *pBackendData, uint64_t uOffset, const void *pvBuf, size_t cbToWrite, size_t *pcbWriteProcess, size_t *pcbPreRead, size_t *pcbPostRead, unsigned fWrite) { LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbToWrite=%zu pcbWriteProcess=%#p pcbPreRead=%#p pcbPostRead=%#p\n", pBackendData, uOffset, pvBuf, cbToWrite, pcbWriteProcess, pcbPreRead, pcbPostRead)); PRAWIMAGE pImage = (PRAWIMAGE)pBackendData; int rc; AssertPtr(pImage); Assert(uOffset % 512 == 0); Assert(cbToWrite % 512 == 0); if (pImage->uOpenFlags & VD_OPEN_FLAGS_READONLY) { rc = VERR_VD_IMAGE_READ_ONLY; goto out; } if ( uOffset + cbToWrite > pImage->cbSize || cbToWrite == 0) { rc = VERR_INVALID_PARAMETER; goto out; } /* For sequential access do not allow to go back. */ if ( pImage->uOpenFlags & VD_OPEN_FLAGS_SEQUENTIAL && uOffset < pImage->offAccess) { rc = VERR_INVALID_PARAMETER; goto out; } rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, uOffset, pvBuf, cbToWrite, NULL); pImage->offAccess = uOffset + cbToWrite; if (pcbWriteProcess) *pcbWriteProcess = cbToWrite; out: LogFlowFunc(("returns %Rrc\n", rc)); return rc; }
/** * Internal: Create a raw image. */ static int rawCreateImage(PRAWIMAGE pImage, uint64_t cbSize, unsigned uImageFlags, const char *pszComment, PCVDGEOMETRY pPCHSGeometry, PCVDGEOMETRY pLCHSGeometry, unsigned uOpenFlags, PFNVDPROGRESS pfnProgress, void *pvUser, unsigned uPercentStart, unsigned uPercentSpan) { int rc; RTFOFF cbFree = 0; uint64_t uOff; void *pvBuf = NULL; int32_t fOpen; uImageFlags |= VD_IMAGE_FLAGS_FIXED; pImage->uOpenFlags = uOpenFlags & ~VD_OPEN_FLAGS_READONLY; pImage->uImageFlags = uImageFlags; pImage->fCreate = true; pImage->PCHSGeometry = *pPCHSGeometry; pImage->LCHSGeometry = *pLCHSGeometry; pImage->pIfError = VDIfErrorGet(pImage->pVDIfsDisk); pImage->pIfIo = VDIfIoIntGet(pImage->pVDIfsImage); AssertPtrReturn(pImage->pIfIo, VERR_INVALID_PARAMETER); if (uImageFlags & VD_IMAGE_FLAGS_DIFF) { rc = vdIfError(pImage->pIfError, VERR_VD_RAW_INVALID_TYPE, RT_SRC_POS, N_("Raw: cannot create diff image '%s'"), pImage->pszFilename); goto out; } /* Create image file. */ fOpen = VDOpenFlagsToFileOpenFlags(pImage->uOpenFlags, true /* fCreate */); if (uOpenFlags & VD_OPEN_FLAGS_SEQUENTIAL) fOpen &= ~RTFILE_O_READ; rc = vdIfIoIntFileOpen(pImage->pIfIo, pImage->pszFilename, fOpen, &pImage->pStorage); if (RT_FAILURE(rc)) { rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("Raw: cannot create image '%s'"), pImage->pszFilename); goto out; } if (!(uOpenFlags & VD_OPEN_FLAGS_SEQUENTIAL)) { /* Check the free space on the disk and leave early if there is not * sufficient space available. */ rc = vdIfIoIntFileGetFreeSpace(pImage->pIfIo, pImage->pszFilename, &cbFree); if (RT_SUCCESS(rc) /* ignore errors */ && ((uint64_t)cbFree < cbSize)) { rc = vdIfError(pImage->pIfError, VERR_DISK_FULL, RT_SRC_POS, N_("Raw: disk would overflow creating image '%s'"), pImage->pszFilename); goto out; } /* Allocate & commit whole file if fixed image, it must be more * effective than expanding file by write operations. */ rc = vdIfIoIntFileSetSize(pImage->pIfIo, pImage->pStorage, cbSize); if (RT_FAILURE(rc)) { rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("Raw: setting image size failed for '%s'"), pImage->pszFilename); goto out; } /* Fill image with zeroes. We do this for every fixed-size image since * on some systems (for example Windows Vista), it takes ages to write * a block near the end of a sparse file and the guest could complain * about an ATA timeout. */ pvBuf = RTMemTmpAllocZ(RAW_FILL_SIZE); if (!pvBuf) { rc = VERR_NO_MEMORY; goto out; } uOff = 0; /* Write data to all image blocks. */ while (uOff < cbSize) { unsigned cbChunk = (unsigned)RT_MIN(cbSize, RAW_FILL_SIZE); rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, uOff, pvBuf, cbChunk, NULL); if (RT_FAILURE(rc)) { rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("Raw: writing block failed for '%s'"), pImage->pszFilename); goto out; } uOff += cbChunk; if (pfnProgress) { rc = pfnProgress(pvUser, uPercentStart + uOff * uPercentSpan * 98 / (cbSize * 100)); if (RT_FAILURE(rc)) goto out; } } } if (RT_SUCCESS(rc) && pfnProgress) pfnProgress(pvUser, uPercentStart + uPercentSpan * 98 / 100); pImage->cbSize = cbSize; rc = rawFlushImage(pImage); out: if (pvBuf) RTMemTmpFree(pvBuf); if (RT_SUCCESS(rc) && pfnProgress) pfnProgress(pvUser, uPercentStart + uPercentSpan); if (RT_FAILURE(rc)) rawFreeImage(pImage, rc != VERR_ALREADY_EXISTS); return rc; }
/** @copydoc VBOXHDDBACKEND::pfnWrite */ static int parallelsWrite(void *pBackendData, uint64_t uOffset, const void *pvBuf, size_t cbToWrite, size_t *pcbWriteProcess, size_t *pcbPreRead, size_t *pcbPostRead, unsigned fWrite) { LogFlowFunc(("pBackendData=%#p uOffset=%llu pvBuf=%#p cbToWrite=%zu pcbWriteProcess=%#p\n", pBackendData, uOffset, pvBuf, cbToWrite, pcbWriteProcess)); PPARALLELSIMAGE pImage = (PPARALLELSIMAGE)pBackendData; int rc = VINF_SUCCESS; AssertPtr(pImage); Assert(uOffset % 512 == 0); Assert(cbToWrite % 512 == 0); if (pImage->uImageFlags & VD_IMAGE_FLAGS_FIXED) rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, uOffset, pvBuf, cbToWrite, NULL); else { uint64_t uSector; uint64_t uOffsetInFile; uint32_t iIndexInAllocationTable; /* Calculate offset in the real file. */ uSector = uOffset / 512; /* One chunk in the file is always one track big. */ iIndexInAllocationTable = (uint32_t)(uSector / pImage->PCHSGeometry.cSectors); uSector = uSector % pImage->PCHSGeometry.cSectors; Assert(iIndexInAllocationTable < pImage->cAllocationBitmapEntries); cbToWrite = RT_MIN(cbToWrite, (pImage->PCHSGeometry.cSectors - uSector)*512); LogFlowFunc(("AllocationBitmap[%u]=%u uSector=%u cbToWrite=%zu cAllocationBitmapEntries=%u\n", iIndexInAllocationTable, pImage->pAllocationBitmap[iIndexInAllocationTable], uSector, cbToWrite, pImage->cAllocationBitmapEntries)); if (pImage->pAllocationBitmap[iIndexInAllocationTable] == 0) { if ( cbToWrite == pImage->PCHSGeometry.cSectors * 512 && !(fWrite & VD_WRITE_NO_ALLOC)) { /* Stay on the safe side. Do not run the risk of confusing the higher * level, as that can be pretty lethal to image consistency. */ *pcbPreRead = 0; *pcbPostRead = 0; /* Allocate new chunk in the file. */ AssertMsg(pImage->cbFileCurrent % 512 == 0, ("File size is not a multiple of 512\n")); pImage->pAllocationBitmap[iIndexInAllocationTable] = (uint32_t)(pImage->cbFileCurrent / 512); pImage->cbFileCurrent += pImage->PCHSGeometry.cSectors * 512; pImage->fAllocationBitmapChanged = true; uOffsetInFile = (uint64_t)pImage->pAllocationBitmap[iIndexInAllocationTable] * 512; LogFlowFunc(("uOffsetInFile=%llu\n", uOffsetInFile)); /* * Write the new block at the current end of the file. */ rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, uOffsetInFile, pvBuf, cbToWrite, NULL); } else { /* Trying to do a partial write to an unallocated cluster. Don't do * anything except letting the upper layer know what to do. */ *pcbPreRead = uSector * 512; *pcbPostRead = (pImage->PCHSGeometry.cSectors * 512) - cbToWrite - *pcbPreRead; rc = VERR_VD_BLOCK_FREE; } } else { uOffsetInFile = ((uint64_t)pImage->pAllocationBitmap[iIndexInAllocationTable] + uSector) * 512; LogFlowFunc(("uOffsetInFile=%llu\n", uOffsetInFile)); rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, uOffsetInFile, pvBuf, cbToWrite, NULL); } } if (pcbWriteProcess) *pcbWriteProcess = cbToWrite; out: LogFlowFunc(("returns %Rrc\n", rc)); return rc; }
/** * Internal: Create a parallels image. */ static int parallelsCreateImage(PPARALLELSIMAGE pImage, uint64_t cbSize, unsigned uImageFlags, const char *pszComment, PCVDGEOMETRY pPCHSGeometry, PCVDGEOMETRY pLCHSGeometry, unsigned uOpenFlags, PFNVDPROGRESS pfnProgress, void *pvUser, unsigned uPercentStart, unsigned uPercentSpan) { int rc = VINF_SUCCESS; int32_t fOpen; if (uImageFlags & VD_IMAGE_FLAGS_FIXED) { rc = vdIfError(pImage->pIfError, VERR_VD_INVALID_TYPE, RT_SRC_POS, N_("Parallels: cannot create fixed image '%s'. Create a raw image"), pImage->pszFilename); goto out; } pImage->uOpenFlags = uOpenFlags & ~VD_OPEN_FLAGS_READONLY; pImage->uImageFlags = uImageFlags; pImage->PCHSGeometry = *pPCHSGeometry; pImage->LCHSGeometry = *pLCHSGeometry; if (!pImage->PCHSGeometry.cCylinders) { /* Set defaults. */ pImage->PCHSGeometry.cSectors = 63; pImage->PCHSGeometry.cHeads = 16; pImage->PCHSGeometry.cCylinders = pImage->cbSize / (512 * pImage->PCHSGeometry.cSectors * pImage->PCHSGeometry.cHeads); } pImage->pIfError = VDIfErrorGet(pImage->pVDIfsDisk); pImage->pIfIo = VDIfIoIntGet(pImage->pVDIfsImage); AssertPtrReturn(pImage->pIfIo, VERR_INVALID_PARAMETER); /* Create image file. */ fOpen = VDOpenFlagsToFileOpenFlags(pImage->uOpenFlags, true /* fCreate */); rc = vdIfIoIntFileOpen(pImage->pIfIo, pImage->pszFilename, fOpen, &pImage->pStorage); if (RT_FAILURE(rc)) { rc = vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("Parallels: cannot create image '%s'"), pImage->pszFilename); goto out; } if (RT_SUCCESS(rc) && pfnProgress) pfnProgress(pvUser, uPercentStart + uPercentSpan * 98 / 100); /* Setup image state. */ pImage->cbSize = cbSize; pImage->cAllocationBitmapEntries = cbSize / 512 / pImage->PCHSGeometry.cSectors; if (pImage->cAllocationBitmapEntries * pImage->PCHSGeometry.cSectors * 512 < cbSize) pImage->cAllocationBitmapEntries++; pImage->fAllocationBitmapChanged = true; pImage->cbFileCurrent = sizeof(ParallelsHeader) + pImage->cAllocationBitmapEntries * sizeof(uint32_t); /* Round to next sector boundary. */ pImage->cbFileCurrent += 512 - pImage->cbFileCurrent % 512; Assert(!(pImage->cbFileCurrent % 512)); pImage->pAllocationBitmap = (uint32_t *)RTMemAllocZ(pImage->cAllocationBitmapEntries * sizeof(uint32_t)); if (!pImage->pAllocationBitmap) rc = VERR_NO_MEMORY; if (RT_SUCCESS(rc)) { ParallelsHeader Header; memcpy(Header.HeaderIdentifier, PARALLELS_HEADER_MAGIC, sizeof(Header.HeaderIdentifier)); Header.uVersion = RT_H2LE_U32(PARALLELS_DISK_VERSION); Header.cHeads = RT_H2LE_U32(pImage->PCHSGeometry.cHeads); Header.cCylinders = RT_H2LE_U32(pImage->PCHSGeometry.cCylinders); Header.cSectorsPerTrack = RT_H2LE_U32(pImage->PCHSGeometry.cSectors); Header.cEntriesInAllocationBitmap = RT_H2LE_U32(pImage->cAllocationBitmapEntries); Header.cSectors = RT_H2LE_U32(pImage->cbSize / 512); memset(Header.Padding, 0, sizeof(Header.Padding)); /* Write header and allocation bitmap. */ rc = vdIfIoIntFileSetSize(pImage->pIfIo, pImage->pStorage, pImage->cbFileCurrent); if (RT_SUCCESS(rc)) rc = vdIfIoIntFileWriteSync(pImage->pIfIo, pImage->pStorage, 0, &Header, sizeof(Header), NULL); if (RT_SUCCESS(rc)) rc = parallelsFlushImage(pImage); /* Writes the allocation bitmap. */ } out: if (RT_SUCCESS(rc) && pfnProgress) pfnProgress(pvUser, uPercentStart + uPercentSpan); if (RT_FAILURE(rc)) parallelsFreeImage(pImage, rc != VERR_ALREADY_EXISTS); return rc; }