feat(vhd-cli compare): compare metadata and content of two VHDs (#5920)

This commit is contained in:
Florent BEAUCHAMP 2021-10-18 16:21:40 +02:00 committed by GitHub
parent 7ef89d5043
commit c62d727cbe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 96 additions and 1 deletions

View File

@ -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 <source VHD> <destination> `
}
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')
})
}

View File

@ -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
}

View File

@ -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'

View File

@ -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)
}
}