@xen-orchestra/backups 0.68.2 → 0.69.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/RemoteAdapter.mjs +3 -3
- package/_cleanVm.mjs +25 -43
- package/_runners/_vmRunners/FullRemote.mjs +3 -2
- package/_runners/_vmRunners/FullXapi.mjs +3 -1
- package/_runners/_vmRunners/IncrementalRemote.mjs +3 -2
- package/_runners/_vmRunners/IncrementalXapi.mjs +8 -1
- package/_runners/_vmRunners/_AbstractRemote.mjs +27 -12
- package/_runners/_vmRunners/_AbstractXapi.mjs +58 -22
- package/_runners/_writers/AggregatedFullRemoteWriter.mjs +38 -0
- package/_runners/_writers/AggregatedFullXapiWriter.mjs +31 -0
- package/_runners/_writers/AggregatedIncrementalRemoteWriter.mjs +78 -0
- package/_runners/_writers/AggregatedIncrementalXapiWriter.mjs +85 -0
- package/_runners/_writers/FullRemoteWriter.mjs +3 -3
- package/_runners/_writers/FullXapiWriter.mjs +3 -3
- package/_runners/_writers/IncrementalRemoteWriter.mjs +2 -2
- package/_runners/_writers/IncrementalXapiWriter.mjs +4 -2
- package/_runners/_writers/_AbstractAggregatedRemoteWriter.mjs +157 -0
- package/_runners/_writers/_AbstractAggregatedXapiWriter.mjs +114 -0
- package/_runners/_writers/_listReplicatedVms.mjs +10 -2
- package/disks/MergeRemoteDisk.mjs +317 -0
- package/disks/RemoteDisk.mjs +204 -0
- package/disks/RemoteVhdDisk.mjs +458 -0
- package/disks/RemoteVhdDiskChain.mjs +271 -0
- package/disks/openDiskChain.mjs +8 -3
- package/package.json +2 -2
- package/disks/RemoteVhd.mjs +0 -171
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @typedef {import('./RemoteVhdDisk.mjs').VhdFooter} VhdFooter
|
|
5
|
+
* @typedef {import('./RemoteVhdDisk.mjs').RemoteVhdDisk} RemoteVhdDisk
|
|
6
|
+
* @typedef {import('@xen-orchestra/disk-transform').DiskBlock} DiskBlock
|
|
7
|
+
* @typedef {import('@xen-orchestra/disk-transform').FileAccessor} FileAccessor
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { RemoteDisk } from './RemoteDisk.mjs'
|
|
11
|
+
|
|
12
|
+
export class RemoteVhdDiskChain extends RemoteDisk {
|
|
13
|
+
/**
|
|
14
|
+
* @type {RemoteVhdDisk[]}
|
|
15
|
+
*/
|
|
16
|
+
#disks
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @type {number}
|
|
20
|
+
*/
|
|
21
|
+
#blockSize = 2 * 1024 * 1024
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @type {number}
|
|
25
|
+
*/
|
|
26
|
+
#headerSize = 1024
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @type {number}
|
|
30
|
+
*/
|
|
31
|
+
#footerSize = 512
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* @type {number}
|
|
35
|
+
*/
|
|
36
|
+
#bitmapSize = 512
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* @param {Object} params
|
|
40
|
+
* @param {FileAccessor} params.handler
|
|
41
|
+
* @param {RemoteVhdDisk[]} params.disks
|
|
42
|
+
*/
|
|
43
|
+
constructor({ disks }) {
|
|
44
|
+
super()
|
|
45
|
+
this.#disks = disks
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Initializes the VHD chain
|
|
50
|
+
* @param {Object} options
|
|
51
|
+
* @param {boolean} options.force
|
|
52
|
+
* @returns {Promise<void>}
|
|
53
|
+
*/
|
|
54
|
+
async init(options) {
|
|
55
|
+
try {
|
|
56
|
+
await Promise.all(this.#disks.map(disk => disk.init(options)))
|
|
57
|
+
let parentUuid = ''
|
|
58
|
+
for (const [index, disk] of this.#disks.entries()) {
|
|
59
|
+
if (index !== 0) {
|
|
60
|
+
if (!disk.isDifferencing()) {
|
|
61
|
+
throw Object.assign(new Error("Can't init vhd directory with non differencing child disks"), {
|
|
62
|
+
code: 'NOT_SUPPORTED',
|
|
63
|
+
})
|
|
64
|
+
}
|
|
65
|
+
if (disk.getParentUuid() !== parentUuid) {
|
|
66
|
+
throw Object.assign(new Error("Can't init vhd directory with incorrect parentage"), {
|
|
67
|
+
code: 'NOT_SUPPORTED',
|
|
68
|
+
})
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
parentUuid = disk.getUuid()
|
|
73
|
+
}
|
|
74
|
+
} catch (error) {
|
|
75
|
+
await this.close()
|
|
76
|
+
throw error
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Closes the VHD.
|
|
82
|
+
* @returns {Promise<void>}
|
|
83
|
+
*/
|
|
84
|
+
async close() {
|
|
85
|
+
await Promise.all(this.#disks.map(disk => disk.close()))
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* @returns {number}
|
|
90
|
+
*/
|
|
91
|
+
getVirtualSize() {
|
|
92
|
+
return this.#disks[this.#disks.length - 1].getVirtualSize()
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* @returns {number} size
|
|
97
|
+
*/
|
|
98
|
+
getSizeOnDisk() {
|
|
99
|
+
const batEntrySize = 4
|
|
100
|
+
const sectorSize = 512
|
|
101
|
+
const batSize = Math.ceil((this.getMaxBlockCount() * batEntrySize) / sectorSize) * sectorSize
|
|
102
|
+
|
|
103
|
+
return (
|
|
104
|
+
this.#footerSize +
|
|
105
|
+
this.#headerSize +
|
|
106
|
+
batSize +
|
|
107
|
+
this.getBlockIndexes().length * (this.#blockSize + this.#bitmapSize) +
|
|
108
|
+
this.#footerSize
|
|
109
|
+
)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* @returns {number}
|
|
114
|
+
*/
|
|
115
|
+
getBlockSize() {
|
|
116
|
+
return this.#disks[this.#disks.length - 1].getBlockSize()
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* @returns {string}
|
|
121
|
+
*/
|
|
122
|
+
getPath() {
|
|
123
|
+
return this.#disks[this.#disks.length - 1].getPath()
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* @returns {string}
|
|
128
|
+
*/
|
|
129
|
+
getUuid() {
|
|
130
|
+
return this.#disks[this.#disks.length - 1].getUuid()
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* @returns {Promise<boolean>} canMergeConcurently
|
|
135
|
+
*/
|
|
136
|
+
async canMergeConcurently() {
|
|
137
|
+
for (const disk of this.#disks) {
|
|
138
|
+
if (!(await disk.isDirectory())) {
|
|
139
|
+
return true
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return false
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* @returns {number} getMaxBlockCount
|
|
147
|
+
*/
|
|
148
|
+
getMaxBlockCount() {
|
|
149
|
+
return this.#disks[this.#disks.length - 1].getMaxBlockCount()
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Checks if the VHD contains a specific block.
|
|
154
|
+
* @param {number} index
|
|
155
|
+
* @returns {boolean}
|
|
156
|
+
*/
|
|
157
|
+
hasBlock(index) {
|
|
158
|
+
for (const disk of this.#disks) {
|
|
159
|
+
if (disk.hasBlock(index)) {
|
|
160
|
+
return true
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return false
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Gets the indexes of all blocks in the VHD.
|
|
169
|
+
* @returns {Array<number>}
|
|
170
|
+
*/
|
|
171
|
+
getBlockIndexes() {
|
|
172
|
+
const indexes = new Set()
|
|
173
|
+
for (const disk of this.#disks) {
|
|
174
|
+
for (const index of disk.getBlockIndexes()) {
|
|
175
|
+
indexes.add(index)
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return [...indexes]
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Returns the parent non inizialized instance
|
|
183
|
+
* @returns {RemoteDisk}
|
|
184
|
+
*/
|
|
185
|
+
instantiateParent() {
|
|
186
|
+
return this.#disks[0].instantiateParent()
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Writes a full block into this VHD.
|
|
191
|
+
* @param {DiskBlock} diskBlock
|
|
192
|
+
* @return {Promise<number>} blockSize
|
|
193
|
+
*/
|
|
194
|
+
async writeBlock(diskBlock) {
|
|
195
|
+
throw new Error(`Can't write blocks into a disk chain`)
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Reads a specific block from the VHD.
|
|
200
|
+
* @param {number} index
|
|
201
|
+
* @returns {Promise<DiskBlock>} diskBlock
|
|
202
|
+
*/
|
|
203
|
+
async readBlock(index) {
|
|
204
|
+
const reversedDisks = this.#disks.slice().reverse()
|
|
205
|
+
for (const disk of reversedDisks) {
|
|
206
|
+
if (disk.hasBlock(index)) {
|
|
207
|
+
return disk.readBlock(index)
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
throw new Error(`Block ${index} not found in chain `)
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Reads a specific block from the child disk to copy/move it to this disk.
|
|
215
|
+
* @param {RemoteDisk} childDisk
|
|
216
|
+
* @param {number} index
|
|
217
|
+
* @param {boolean} isResumingMerge
|
|
218
|
+
* @returns {Promise<number>} blockSize
|
|
219
|
+
*/
|
|
220
|
+
async mergeBlock(childDisk, index, isResumingMerge) {
|
|
221
|
+
throw new Error(`Can't merge block into a disk chain`)
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* @returns {VhdFooter}
|
|
226
|
+
*/
|
|
227
|
+
getMetadata() {
|
|
228
|
+
return this.#disks[this.#disks.length - 1].getMetadata()
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* @param {RemoteVhdDisk} childDisk
|
|
233
|
+
* @returns {Promise<void>}
|
|
234
|
+
*/
|
|
235
|
+
async flushMetadata(childDisk) {
|
|
236
|
+
throw new Error(`Can't flush metadata on a disk chain`)
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* @param {RemoteVhdDisk} childDisk
|
|
241
|
+
*/
|
|
242
|
+
mergeMetadata(childDisk) {
|
|
243
|
+
throw new Error(`Can't merge metadata on a disk chain`)
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Checks if the VHD is a differencing disk.
|
|
248
|
+
* @returns {boolean}
|
|
249
|
+
*/
|
|
250
|
+
isDifferencing() {
|
|
251
|
+
return this.#disks[0].isDifferencing()
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Abstract
|
|
256
|
+
* Rename alias/disk
|
|
257
|
+
* @param {string} newPath
|
|
258
|
+
*/
|
|
259
|
+
async rename(newPath) {
|
|
260
|
+
throw new Error(`Can't rename a disk chain`)
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Deletes all the disks
|
|
265
|
+
*/
|
|
266
|
+
async unlink() {
|
|
267
|
+
for (const disk of this.#disks) {
|
|
268
|
+
await disk.unlink()
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
package/disks/openDiskChain.mjs
CHANGED
|
@@ -2,12 +2,14 @@
|
|
|
2
2
|
/**
|
|
3
3
|
*
|
|
4
4
|
* @typedef {import('../../disk-transform/src/FileAccessor.mjs').FileAccessor} FileAccessor
|
|
5
|
+
* @typedef {import('./RemoteDisk.mjs').RemoteDisk} RemoteDisk
|
|
5
6
|
*/
|
|
6
7
|
import { DiskChain } from '@xen-orchestra/disk-transform'
|
|
7
|
-
import {
|
|
8
|
+
import { RemoteVhdDisk } from './RemoteVhdDisk.mjs'
|
|
8
9
|
|
|
9
10
|
import { defer } from 'golike-defer'
|
|
10
11
|
/**
|
|
12
|
+
* @param {any} $defer
|
|
11
13
|
* @param {Object} params
|
|
12
14
|
* @param {FileAccessor} params.handler
|
|
13
15
|
* @param {string} params.path
|
|
@@ -15,15 +17,18 @@ import { defer } from 'golike-defer'
|
|
|
15
17
|
*/
|
|
16
18
|
async function _openDiskChain($defer, { handler, path, until }) {
|
|
17
19
|
let disk
|
|
20
|
+
/**
|
|
21
|
+
* @type {Array<RemoteDisk>}
|
|
22
|
+
*/
|
|
18
23
|
const disks = []
|
|
19
24
|
$defer.onFailure(() => Promise.all(disks.map(disk => disk.close())))
|
|
20
|
-
disk = new
|
|
25
|
+
disk = new RemoteVhdDisk({ handler, path })
|
|
21
26
|
|
|
22
27
|
await disk.init()
|
|
23
28
|
disks.push(disk)
|
|
24
29
|
while (disk.isDifferencing()) {
|
|
25
30
|
disk = await disk.openParent()
|
|
26
|
-
if (disk.
|
|
31
|
+
if (disk.getPath() === until) {
|
|
27
32
|
break
|
|
28
33
|
}
|
|
29
34
|
disks.unshift(disk)
|
package/package.json
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"type": "git",
|
|
9
9
|
"url": "https://github.com/vatesfr/xen-orchestra.git"
|
|
10
10
|
},
|
|
11
|
-
"version": "0.
|
|
11
|
+
"version": "0.69.1",
|
|
12
12
|
"engines": {
|
|
13
13
|
"node": ">=14.18"
|
|
14
14
|
},
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"@vates/parse-duration": "^0.1.1",
|
|
31
31
|
"@xen-orchestra/async-map": "^0.1.2",
|
|
32
32
|
"@xen-orchestra/disk-transform": "^1.2.1",
|
|
33
|
-
"@xen-orchestra/fs": "^4.6.
|
|
33
|
+
"@xen-orchestra/fs": "^4.6.7",
|
|
34
34
|
"@xen-orchestra/log": "^0.7.1",
|
|
35
35
|
"@xen-orchestra/qcow2": "^1.1.2",
|
|
36
36
|
"@xen-orchestra/template": "^0.1.0",
|
package/disks/RemoteVhd.mjs
DELETED
|
@@ -1,171 +0,0 @@
|
|
|
1
|
-
// @ts-check
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* @typedef {import('@xen-orchestra/disk-transform').FileAccessor} FileAccessor
|
|
5
|
-
* @typedef {import('@xen-orchestra/disk-transform').DiskBlock} DiskBlock
|
|
6
|
-
* @typedef {import('@xen-orchestra/disk-transform').Disk} Disk
|
|
7
|
-
* @typedef {import('vhd-lib/Vhd/VhdDirectory.js').VhdDirectory} VhdDirectory
|
|
8
|
-
* @typedef {import('vhd-lib/Vhd/VhdFile.js').VhdFile} VhdFile
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import { openVhd } from 'vhd-lib'
|
|
12
|
-
import { DISK_TYPES } from 'vhd-lib/_constants.js'
|
|
13
|
-
import { dirname, join } from 'node:path'
|
|
14
|
-
import { RandomAccessDisk } from '@xen-orchestra/disk-transform'
|
|
15
|
-
/**
|
|
16
|
-
* Represents a remote VHD (Virtual Hard Disk) that extends RandomAccessDisk.
|
|
17
|
-
*/
|
|
18
|
-
export class RemoteVhd extends RandomAccessDisk {
|
|
19
|
-
/**
|
|
20
|
-
* @type {string}
|
|
21
|
-
*/
|
|
22
|
-
#path
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* @type {FileAccessor}
|
|
26
|
-
*/
|
|
27
|
-
#handler
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* @type {VhdFile | VhdDirectory | undefined}
|
|
31
|
-
*/
|
|
32
|
-
#vhd
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* @type {boolean | undefined}
|
|
36
|
-
*/
|
|
37
|
-
#isDifferencing
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* @type {() => any}
|
|
41
|
-
*/
|
|
42
|
-
#dispose = () => {}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* @returns {string}
|
|
46
|
-
*/
|
|
47
|
-
get path() {
|
|
48
|
-
return this.#path
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* @param {Object} params
|
|
53
|
-
* @param {FileAccessor} params.handler
|
|
54
|
-
* @param {string} params.path
|
|
55
|
-
*/
|
|
56
|
-
constructor({ handler, path }) {
|
|
57
|
-
super()
|
|
58
|
-
// @todo : ensure this is the full path from the root of the remote
|
|
59
|
-
this.#path = path
|
|
60
|
-
this.#handler = handler
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* @returns {number}
|
|
65
|
-
*/
|
|
66
|
-
getVirtualSize() {
|
|
67
|
-
if (this.#vhd === undefined) {
|
|
68
|
-
throw new Error(`can't call getvirtualsize of a RemoteVhd before init`)
|
|
69
|
-
}
|
|
70
|
-
return this.#vhd.footer.currentSize
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* @returns {number}
|
|
75
|
-
*/
|
|
76
|
-
getBlockSize() {
|
|
77
|
-
return 2 * 1024 * 1024
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Initializes the VHD.
|
|
82
|
-
* @returns {Promise<void>}
|
|
83
|
-
*/
|
|
84
|
-
async init() {
|
|
85
|
-
const { value, dispose } = await openVhd(this.#handler, this.#path)
|
|
86
|
-
this.#vhd = value
|
|
87
|
-
this.#dispose = dispose
|
|
88
|
-
await this.#vhd.readBlockAllocationTable()
|
|
89
|
-
this.#isDifferencing = value.footer.diskType === DISK_TYPES.DIFFERENCING
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* Closes the VHD.
|
|
94
|
-
* @returns {Promise<void>}
|
|
95
|
-
*/
|
|
96
|
-
async close() {
|
|
97
|
-
await this.#dispose()
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* Checks if the VHD contains a specific block.
|
|
102
|
-
* @param {number} index
|
|
103
|
-
* @returns {boolean}
|
|
104
|
-
*/
|
|
105
|
-
hasBlock(index) {
|
|
106
|
-
if (this.#vhd === undefined) {
|
|
107
|
-
throw new Error(`can't call hasblock of a RemoteVhd before init`)
|
|
108
|
-
}
|
|
109
|
-
return this.#vhd.containsBlock(index)
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* Gets the indexes of all blocks in the VHD.
|
|
114
|
-
* @returns {Array<number>}
|
|
115
|
-
*/
|
|
116
|
-
getBlockIndexes() {
|
|
117
|
-
if (this.#vhd === undefined) {
|
|
118
|
-
throw new Error(`can't call getBlockIndexes of a RemoteVhd before init`)
|
|
119
|
-
}
|
|
120
|
-
const index = []
|
|
121
|
-
for (let blockIndex = 0; blockIndex < this.#vhd.header.maxTableEntries; blockIndex++) {
|
|
122
|
-
if (this.hasBlock(blockIndex)) {
|
|
123
|
-
index.push(blockIndex)
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
return index
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* Reads a specific block from the VHD.
|
|
131
|
-
* @param {number} index
|
|
132
|
-
* @returns {Promise<DiskBlock>}
|
|
133
|
-
*/
|
|
134
|
-
async readBlock(index) {
|
|
135
|
-
if (this.#vhd === undefined) {
|
|
136
|
-
throw new Error(`can't call readBlock of a RemoteVhd before init`)
|
|
137
|
-
}
|
|
138
|
-
const { data } = await this.#vhd.readBlock(index)
|
|
139
|
-
return {
|
|
140
|
-
index,
|
|
141
|
-
data,
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
/**
|
|
145
|
-
*
|
|
146
|
-
* @returns {RandomAccessDisk}
|
|
147
|
-
*/
|
|
148
|
-
instantiateParent() {
|
|
149
|
-
if (this.#vhd === undefined) {
|
|
150
|
-
throw new Error(`can't call openParent of a RemoteVhd before init`)
|
|
151
|
-
}
|
|
152
|
-
const parentPath = this.#vhd.header.parentUnicodeName
|
|
153
|
-
const fullParentPath = join(dirname(this.#path), parentPath)
|
|
154
|
-
if (!parentPath) {
|
|
155
|
-
throw new Error(`Disk ${this.#path} doesn't have parents`)
|
|
156
|
-
}
|
|
157
|
-
const parent = new RemoteVhd({ handler: this.#handler, path: fullParentPath })
|
|
158
|
-
return parent
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
/**
|
|
162
|
-
* Checks if the VHD is a differencing disk.
|
|
163
|
-
* @returns {boolean}
|
|
164
|
-
*/
|
|
165
|
-
isDifferencing() {
|
|
166
|
-
if (this.#isDifferencing === undefined) {
|
|
167
|
-
throw new Error(`can't call isDifferencing of a RemoteVhd before init`)
|
|
168
|
-
}
|
|
169
|
-
return this.#isDifferencing
|
|
170
|
-
}
|
|
171
|
-
}
|