diff --git a/@vates/nbd-client/NbdDisk.mjs b/@vates/nbd-client/NbdDisk.mjs new file mode 100644 index 0000000000..a8c2c52820 --- /dev/null +++ b/@vates/nbd-client/NbdDisk.mjs @@ -0,0 +1,122 @@ +import { RandomAccessDisk } from '@xen-orchestra/disk-transform' +import NbdClient from '@vates/nbd-client' + +/** + * @typedef {{ offset: number, length: number }} DataRange + */ + +/** + * @extends {RandomAccessDisk} + */ +export class NbdDisk extends RandomAccessDisk { + #nbdInfos + /** @type {NbdClient|undefined} */ + #nbdClient + + /** @type {Array | undefined} */ + #dataMap + + /** @type {number} */ + #blockSize + + constructor(nbdInfos, blockSize, { dataMap } = {}) { + super() + this.#blockSize = blockSize + this.#nbdInfos = nbdInfos + this.#dataMap = dataMap && this.#processDatamap(dataMap) + } + + #processDatamap(rawDataMap) { + return rawDataMap + .filter(({ type }) => type === 0) + .map(({ offset, length }) => ({ offset, length })) + .sort(({ offset: offset1 }, { offset: offset2 }) => offset1 - offset2) + } + /** + * @param {number} index + * @returns {Promise} + */ + async readBlock(index) { + if (!this.#nbdClient) { + throw new Error("can't readBlock before init") + } + const data = await this.#nbdClient.readBlock(index, this.getBlockSize()) + return { + index, + data, + } + } + + /** + * @returns {number} + */ + getVirtualSize() { + if (!this.#nbdClient) { + throw new Error("can't get size before init") + } + return Number(this.#nbdClient.exportSize) + } + + /** + * @returns {number} + */ + getBlockSize() { + return this.#blockSize + } + + /** + * @returns {Promise} + */ + async init() { + this.#nbdClient = new NbdClient(this.#nbdInfos) + await this.#nbdClient.connect() + if (this.#dataMap === undefined) { + this.#dataMap = this.#processDatamap(await this.#nbdClient.getMap()) + } + } + + /** + * @returns {Promise} + */ + async close() { + await this.#nbdClient?.disconnect() + } + + /** + * @returns {boolean} + */ + isDifferencing() { + return false + } + + /** + * @returns {Array} + */ + getBlockIndexes() { + const indexes = new Set() + this.#dataMap?.forEach(({ offset, length }) => { + if (length === 0) return + + const firstBlockIndex = Math.floor(offset / this.getBlockSize()) + const lastBlockIndex = Math.floor((offset + length - 1) / this.getBlockSize()) + + for (let blockIndex = firstBlockIndex; blockIndex <= lastBlockIndex; blockIndex++) { + indexes.add(blockIndex) + } + }) + return [...indexes].sort((a, b) => a - b) + } + + /** + * @param {number} index + * @returns {boolean} + */ + hasBlock(index) { + if (!this.#dataMap) { + throw new Error("can't hasBlock before init") + } + const blockStart = index * this.getBlockSize() + const blockEnd = (index + 1) * this.getBlockSize() + return this.#dataMap.some(({ offset, length }) => offset + length > blockStart && blockEnd >= offset) + } +} diff --git a/@vates/nbd-client/package.json b/@vates/nbd-client/package.json index b9ed78060f..35374dd351 100644 --- a/@vates/nbd-client/package.json +++ b/@vates/nbd-client/package.json @@ -22,6 +22,7 @@ "@vates/async-each": "^1.0.0", "@vates/read-chunk": "^1.2.0", "@xen-orchestra/async-map": "^0.1.2", + "@xen-orchestra/disk-transform": "^1.0.1", "@xen-orchestra/log": "^0.7.1", "promise-toolbox": "^0.21.0", "xen-api": "^4.7.3"