diff --git a/packages/vhd-cli/src/commands/compare.js b/packages/vhd-cli/src/commands/compare.js new file mode 100644 index 000000000..af3c85b78 --- /dev/null +++ b/packages/vhd-cli/src/commands/compare.js @@ -0,0 +1,81 @@ +import { getSyncedHandler } from '@xen-orchestra/fs' +import { openVhd, Constants } from 'vhd-lib' +import { resolve } from 'path' +import Disposable from 'promise-toolbox/Disposable' +import omit from 'lodash/omit' + +const deepCompareObjects = function (src, dest, path) { + for (const key of Object.keys(src)) { + const srcValue = src[key] + const destValue = dest[key] + if (srcValue !== destValue) { + const srcType = typeof srcValue + const destType = typeof destValue + if (srcType !== destType) { + throw new Error(`key ${path + '/' + key} is of type *${srcType}* in source and *${destType}* in dest`) + } + + if (srcType !== 'object') { + throw new Error(`key ${path + '/' + key} is *${srcValue}* in source and *${destValue}* in dest`) + } + + if (Buffer.isBuffer(srcValue)) { + if (!(Buffer.isBuffer(destValue) && srcValue.equals(destValue))) { + throw new Error(`key ${path + '/' + key} is buffer in source that does not equal dest`) + } + } else { + deepCompareObjects(src[key], dest[key], path + '/' + key) + } + } + } +} + +export default async args => { + if (args.length < 2 || args.some(_ => _ === '-h' || _ === '--help')) { + return `Usage: compare ` + } + const [sourcePath, destPath] = args + + await Disposable.use(async function* () { + const handler = yield getSyncedHandler({ url: 'file:///' }) + const src = yield openVhd(handler, resolve(sourcePath)) + const dest = yield openVhd(handler, resolve(destPath)) + + // parent locator entries contains offset that can be different without impacting the vhd + // we'll compare them later + // table offset and checksum are also implementation specific + const ignoredEntries = ['checksum', 'parentLocatorEntry', 'tableOffset'] + deepCompareObjects(omit(src.header, ignoredEntries), omit(dest.header, ignoredEntries), 'header') + deepCompareObjects(src.footer, dest.footer, 'footer') + + await src.readBlockAllocationTable() + await dest.readBlockAllocationTable() + + for (let i = 0; i < src.header.maxTableEntries; i++) { + if (src.containsBlock(i)) { + if (dest.containsBlock(i)) { + const srcBlock = await src.readBlock(i) + const destBlock = await dest.readBlock(i) + + if (!srcBlock.buffer.equals(destBlock.buffer)) { + throw new Error(`Block ${i} has different data in src and dest`) + } + } else { + throw new Error(`Block ${i} is present in source but not in dest `) + } + } else if (dest.containsBlock(i)) { + throw new Error(`Block ${i} is present in dest but not in source `) + } + } + + for (let parentLocatorId = 0; parentLocatorId < Constants.PARENT_LOCATOR_ENTRIES; parentLocatorId++) { + const srcParentLocator = await src.readParentLocator(parentLocatorId) + const destParentLocator = await dest.readParentLocator(parentLocatorId) + if (!srcParentLocator.data || !srcParentLocator.data.equals(destParentLocator.data)) { + console.log(srcParentLocator, destParentLocator) + throw new Error(`Parent Locator ${parentLocatorId} has different data in src and dest`) + } + } + console.log('there is no difference between theses vhd') + }) +} diff --git a/packages/vhd-lib/src/Vhd/VhdFile.js b/packages/vhd-lib/src/Vhd/VhdFile.js index 6bb0b3cab..72cea685b 100644 --- a/packages/vhd-lib/src/Vhd/VhdFile.js +++ b/packages/vhd-lib/src/Vhd/VhdFile.js @@ -54,7 +54,7 @@ export class VhdFile extends VhdAbstract { #uncheckedBlockTable get #blocktable() { - assert.notStrictEqual(this.#blockTable, undefined, 'Block table must be initialized before access') + assert.notStrictEqual(this.#uncheckedBlockTable, undefined, 'Block table must be initialized before access') return this.#uncheckedBlockTable } diff --git a/packages/vhd-lib/src/index.js b/packages/vhd-lib/src/index.js index fc94ce5ef..ec76498e8 100644 --- a/packages/vhd-lib/src/index.js +++ b/packages/vhd-lib/src/index.js @@ -8,5 +8,7 @@ export { default as createSyntheticStream } from './createSyntheticStream' export { default as createVhdStreamWithLength } from './createVhdStreamWithLength' export { default as mergeVhd } from './merge' export { default as peekFooterFromVhdStream } from './peekFooterFromVhdStream' +export { openVhd } from './openVhd' export { VhdDirectory } from './Vhd/VhdDirectory' export { VhdFile } from './Vhd/VhdFile' +export * as Constants from './_constants' diff --git a/packages/vhd-lib/src/openVhd.js b/packages/vhd-lib/src/openVhd.js new file mode 100644 index 000000000..3bfef44c0 --- /dev/null +++ b/packages/vhd-lib/src/openVhd.js @@ -0,0 +1,12 @@ +import { VhdFile, VhdDirectory } from './' + +export async function openVhd(handler, path) { + try { + return await VhdFile.open(handler, path) + } catch (e) { + if (e.code !== 'EISDIR') { + throw e + } + return await VhdDirectory.open(handler, path) + } +}