UNPKG

xo-vmdk-to-vhd

Version:
159 lines (158 loc) 7.62 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.generateVmdkData = generateVmdkData; var assert = _interopRequireWildcard(require("assert")); var _zlib = _interopRequireDefault(require("zlib")); var _definitions = require("./definitions"); function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); } function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } const roundToSector = value => Math.ceil(value / _definitions.SECTOR_SIZE) * _definitions.SECTOR_SIZE; async function generateVmdkData(diskName, diskCapacityBytes, blockSizeBytes, blockGenerator, geometry = { sectorsPerTrackCylinder: 63, heads: 16, cylinders: 10402 }, dataSize) { const cid = Math.floor(Math.random() * Math.pow(2, 32)); const diskCapacitySectors = Math.ceil(diskCapacityBytes / _definitions.SECTOR_SIZE); const descriptor = `# Disk DescriptorFile version=1 CID=${cid} parentCID=ffffffff createType="streamOptimized" # Extent description RW ${diskCapacitySectors} SPARSE "${diskName}" # The Disk Data Base #DDB ddb.adapterType = "ide" ddb.geometry.sectors = "${geometry.sectorsPerTrackCylinder}" ddb.geometry.heads = "${geometry.heads}" ddb.geometry.cylinders = "${geometry.cylinders}" `; const utf8Descriptor = Buffer.from(descriptor, 'utf8'); const descriptorSizeSectors = Math.ceil(utf8Descriptor.length / _definitions.SECTOR_SIZE) + 10; const descriptorBuffer = Buffer.alloc(descriptorSizeSectors * _definitions.SECTOR_SIZE); utf8Descriptor.copy(descriptorBuffer); const headerData = (0, _definitions.createStreamOptimizedHeader)(diskCapacitySectors, descriptorSizeSectors); const parsedHeader = (0, _definitions.unpackHeader)(headerData.buffer); const grainSizeBytes = parsedHeader.grainSizeSectors * _definitions.SECTOR_SIZE; if (blockSizeBytes % grainSizeBytes !== 0 || blockSizeBytes === 0) { throw new Error(`createReadableVmdkStream can only accept block size multiple of ${grainSizeBytes}, got ${blockSizeBytes}`); } const grainTableEntries = headerData.grainTableEntries; const tableBuffer = Buffer.alloc(grainTableEntries * 4); let streamPosition = 0; let directoryOffset = 0; const endMetadataLength = computeEndMetadataLength(); const metadataSize = headerData.buffer.length + descriptorBuffer.length + endMetadataLength; function track(buffer) { assert.equal(streamPosition % _definitions.SECTOR_SIZE, 0); if (buffer.length > 0) { streamPosition += buffer.length; } return buffer; } function createEmptyMarker(type) { const buff = Buffer.alloc(_definitions.SECTOR_SIZE); buff.writeBigUInt64LE(BigInt(0), 0); buff.writeUInt32LE(0, 8); buff.writeUInt32LE(type, 12); return buff; } function createDirectoryBuffer(grainDirectoryEntries, tablePosition) { const OFFSET_SIZE = 4; directoryOffset = streamPosition; const buff = Buffer.alloc(roundToSector(grainDirectoryEntries * OFFSET_SIZE)); for (let i = 0; i < grainDirectoryEntries; i++) { buff.writeUInt32LE((tablePosition + i * parsedHeader.numGTEsPerGT * OFFSET_SIZE) / _definitions.SECTOR_SIZE, i * OFFSET_SIZE); } return buff; } function bufferIsBlank(buffer) { for (const b of buffer) { if (b !== 0) { return false; } } return true; } function createMarkedGrain(lbaBytes, buffer) { assert.strictEqual(buffer.length, grainSizeBytes); assert.strictEqual(lbaBytes % grainSizeBytes, 0); const markerOverHead = 12; const compressed = _zlib.default.deflateSync(buffer, { level: _zlib.default.constants.Z_BEST_SPEED }); const outputBuffer = Buffer.alloc(roundToSector(markerOverHead + compressed.length)); compressed.copy(outputBuffer, markerOverHead); outputBuffer.writeBigUInt64LE(BigInt(lbaBytes / _definitions.SECTOR_SIZE), 0); outputBuffer.writeUInt32LE(compressed.length, 8); return outputBuffer; } async function* emitBlock(blockLbaBytes, buffer, grainSizeBytes) { assert.strictEqual(buffer.length % grainSizeBytes, 0); const grainCount = buffer.length / grainSizeBytes; for (let i = 0; i < grainCount; i++) { const grainLbaBytes = blockLbaBytes + i * grainSizeBytes; const tableIndex = grainLbaBytes / grainSizeBytes; const grainData = buffer.slice(i * grainSizeBytes, (i + 1) * grainSizeBytes); if (!bufferIsBlank(grainData)) { tableBuffer.writeUInt32LE(streamPosition / _definitions.SECTOR_SIZE, tableIndex * 4); yield track(createMarkedGrain(grainLbaBytes, grainData)); } } } async function* emitBlocks(grainSize, blockGenerator) { for await (const b of blockGenerator) { yield* emitBlock(b.lba, b.block, grainSize); } } function computeEndMetadataLength() { return _definitions.SECTOR_SIZE + roundToSector(tableBuffer.length) + _definitions.SECTOR_SIZE + roundToSector(headerData.grainDirectoryEntries * 4) + _definitions.SECTOR_SIZE + roundToSector(tableBuffer.length) + _definitions.SECTOR_SIZE + roundToSector(headerData.grainDirectoryEntries * 4) + _definitions.SECTOR_SIZE + _definitions.SECTOR_SIZE + _definitions.SECTOR_SIZE; } function* padding() { if (dataSize === undefined) { return; } const targetSize = dataSize + metadataSize; let remaining = targetSize - streamPosition - endMetadataLength; if (remaining < 0) { throw new Error(`vmdk is bigger than precalculed size`); } const size = 1024 * 1024; const fullBuffer = Buffer.alloc(size, 0); while (remaining > size) { yield track(fullBuffer); remaining -= size; } yield track(Buffer.alloc(remaining)); } async function* iterator() { yield track(headerData.buffer); yield track(descriptorBuffer); yield* emitBlocks(grainSizeBytes, blockGenerator); yield* padding(); yield track(createEmptyMarker(_definitions.MARKER_GT)); let tableOffset = streamPosition; yield track(tableBuffer); yield track(createEmptyMarker(_definitions.MARKER_GD)); yield track(createDirectoryBuffer(headerData.grainDirectoryEntries, tableOffset)); const rDirectoryOffset = directoryOffset; yield track(createEmptyMarker(_definitions.MARKER_GT)); tableOffset = streamPosition; yield track(tableBuffer); yield track(createEmptyMarker(_definitions.MARKER_GD)); yield track(createDirectoryBuffer(headerData.grainDirectoryEntries, tableOffset)); yield track(createEmptyMarker(_definitions.MARKER_FOOTER)); const footer = (0, _definitions.createStreamOptimizedHeader)(diskCapacitySectors, descriptorSizeSectors, directoryOffset / _definitions.SECTOR_SIZE, rDirectoryOffset / _definitions.SECTOR_SIZE); yield track(footer.buffer); yield track(createEmptyMarker(_definitions.MARKER_EOS)); } return { iterator: iterator(), metadataSize }; } //# sourceMappingURL=vmdk-generate.js.map