@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.
@@ -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
+ }
@@ -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 { RemoteVhd } from './RemoteVhd.mjs'
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 RemoteVhd({ handler, path })
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.path === until) {
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.68.2",
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.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",
@@ -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
- }