xo-vmdk-to-vhd
Version:
JS lib reading and writing .vmdk and .ova files
159 lines (158 loc) • 7.62 kB
JavaScript
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
;