mirror of
https://github.com/vatesfr/xen-orchestra.git
synced 2026-11-09 17:06:13 -06:00
fix(disk-transform): erronous offset in DiskLargerBlock when copying data
the offset must be relative to the start of THIS large block, not to the start of the disk — otherwise every large block after index 0 lands past the end of destinationBlock and copy() silently writes nothing (zeroed block). affect qcow2 incremental backup with NBD disable NBD + qcow2 and Vhd backups don't go through this code path
This commit is contained in:
committed by
Florent BEAUCHAMP
parent
f683817dec
commit
9388fa4bb6
@@ -33,9 +33,10 @@ export class DiskLargerBlock extends RandomAccessDisk {
|
||||
async readBlock(index: number): Promise<DiskBlock> {
|
||||
// @todo handle partial block at the end
|
||||
const source = this.source
|
||||
const destinationBlock = Buffer.alloc(this.getBlockSize(), 0)
|
||||
const destinationBlockData = Buffer.alloc(this.getBlockSize(), 0)
|
||||
const blockRatio = this.#blockSize / source.getBlockSize()
|
||||
for (let i = index * blockRatio; i < (index + 1) * blockRatio; i++) {
|
||||
const firstSourceBlockIndex = index * blockRatio
|
||||
for (let i = firstSourceBlockIndex; i < firstSourceBlockIndex + blockRatio; i++) {
|
||||
let data: Buffer | undefined
|
||||
if (source.hasBlock(i)) {
|
||||
data = (await source.readBlock(i)).data
|
||||
@@ -51,12 +52,12 @@ export class DiskLargerBlock extends RandomAccessDisk {
|
||||
}
|
||||
}
|
||||
if (data !== undefined) {
|
||||
data.copy(destinationBlock, i * source.getBlockSize())
|
||||
data.copy(destinationBlockData, (i - firstSourceBlockIndex) * source.getBlockSize())
|
||||
}
|
||||
}
|
||||
return {
|
||||
index,
|
||||
data: destinationBlock,
|
||||
data: destinationBlockData,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -118,6 +118,29 @@ test('readBlock with simple block mapping', async () => {
|
||||
assert(result.data.subarray(512).equals(block2))
|
||||
})
|
||||
|
||||
test('readBlock at index > 0 places source data at block-relative offsets', async () => {
|
||||
// Regression test: a large block after index 0 must be assembled from its own
|
||||
// source blocks at offsets relative to the start of that large block.
|
||||
// With distinct, non-zero patterns this catches the case where copy() targets an
|
||||
// absolute (disk-wide) offset and silently produces a zeroed block.
|
||||
const blockC = createPatternBuffer(512, 'C')
|
||||
const blockD = createPatternBuffer(512, 'D')
|
||||
const source = new MockDisk(512, 4096, [
|
||||
[2, blockC],
|
||||
[3, blockD],
|
||||
])
|
||||
await source.init()
|
||||
|
||||
const disk = new DiskLargerBlock(source, 1024) // blockRatio = 2, so large block 1 = source blocks 2 & 3
|
||||
await disk.init()
|
||||
|
||||
const result = await disk.readBlock(1)
|
||||
assert.strictEqual(result.index, 1)
|
||||
assert.strictEqual(result.data.length, 1024)
|
||||
assert(result.data.subarray(0, 512).equals(blockC))
|
||||
assert(result.data.subarray(512).equals(blockD))
|
||||
})
|
||||
|
||||
test('hasBlock behavior', async () => {
|
||||
const source = new MockDisk(512, 2048, [
|
||||
[0, Buffer.alloc(512)],
|
||||
|
||||
Reference in New Issue
Block a user