]> git.proxmox.com Git - pve-qemu.git/blob - debian/patches/pve/0038-PVE-Add-PBS-block-driver-to-map-backup-archives-into.patch
clean up pve/ patches by squashing patches of patches
[pve-qemu.git] / debian / patches / pve / 0038-PVE-Add-PBS-block-driver-to-map-backup-archives-into.patch
1 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
2 From: Stefan Reiter <s.reiter@proxmox.com>
3 Date: Wed, 8 Jul 2020 09:50:54 +0200
4 Subject: [PATCH] PVE: Add PBS block driver to map backup archives into VMs
5
6 Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
7 [error cleanups, file_open implementation]
8 Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
9 ---
10 block/meson.build | 3 +
11 block/pbs.c | 271 +++++++++++++++++++++++++++++++++++++++++++
12 configure | 9 ++
13 meson.build | 1 +
14 qapi/block-core.json | 14 ++-
15 5 files changed, 297 insertions(+), 1 deletion(-)
16 create mode 100644 block/pbs.c
17
18 diff --git a/block/meson.build b/block/meson.build
19 index dfae565db3..a070060e53 100644
20 --- a/block/meson.build
21 +++ b/block/meson.build
22 @@ -49,6 +49,9 @@ block_ss.add(files(
23 '../pve-backup.c',
24 ), libproxmox_backup_qemu)
25
26 +block_ss.add(when: 'CONFIG_PBS_BDRV', if_true: files('pbs.c'))
27 +block_ss.add(when: 'CONFIG_PBS_BDRV', if_true: libproxmox_backup_qemu)
28 +
29
30 softmmu_ss.add(when: 'CONFIG_TCG', if_true: files('blkreplay.c'))
31
32 diff --git a/block/pbs.c b/block/pbs.c
33 new file mode 100644
34 index 0000000000..1481a2bfd1
35 --- /dev/null
36 +++ b/block/pbs.c
37 @@ -0,0 +1,271 @@
38 +/*
39 + * Proxmox Backup Server read-only block driver
40 + */
41 +
42 +#include "qemu/osdep.h"
43 +#include "qapi/error.h"
44 +#include "qapi/qmp/qdict.h"
45 +#include "qapi/qmp/qstring.h"
46 +#include "qemu/module.h"
47 +#include "qemu/option.h"
48 +#include "qemu/cutils.h"
49 +#include "block/block_int.h"
50 +
51 +#include <proxmox-backup-qemu.h>
52 +
53 +#define PBS_OPT_REPOSITORY "repository"
54 +#define PBS_OPT_SNAPSHOT "snapshot"
55 +#define PBS_OPT_ARCHIVE "archive"
56 +#define PBS_OPT_KEYFILE "keyfile"
57 +#define PBS_OPT_PASSWORD "password"
58 +#define PBS_OPT_FINGERPRINT "fingerprint"
59 +#define PBS_OPT_ENCRYPTION_PASSWORD "key_password"
60 +
61 +typedef struct {
62 + ProxmoxRestoreHandle *conn;
63 + char aid;
64 + int64_t length;
65 +
66 + char *repository;
67 + char *snapshot;
68 + char *archive;
69 +} BDRVPBSState;
70 +
71 +static QemuOptsList runtime_opts = {
72 + .name = "pbs",
73 + .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
74 + .desc = {
75 + {
76 + .name = PBS_OPT_REPOSITORY,
77 + .type = QEMU_OPT_STRING,
78 + .help = "The server address and repository to connect to.",
79 + },
80 + {
81 + .name = PBS_OPT_SNAPSHOT,
82 + .type = QEMU_OPT_STRING,
83 + .help = "The snapshot to read.",
84 + },
85 + {
86 + .name = PBS_OPT_ARCHIVE,
87 + .type = QEMU_OPT_STRING,
88 + .help = "Which archive within the snapshot should be accessed.",
89 + },
90 + {
91 + .name = PBS_OPT_PASSWORD,
92 + .type = QEMU_OPT_STRING,
93 + .help = "Server password. Can be passed as env var 'PBS_PASSWORD'.",
94 + },
95 + {
96 + .name = PBS_OPT_FINGERPRINT,
97 + .type = QEMU_OPT_STRING,
98 + .help = "Server fingerprint. Can be passed as env var 'PBS_FINGERPRINT'.",
99 + },
100 + {
101 + .name = PBS_OPT_ENCRYPTION_PASSWORD,
102 + .type = QEMU_OPT_STRING,
103 + .help = "Optional: Key password. Can be passed as env var 'PBS_ENCRYPTION_PASSWORD'.",
104 + },
105 + {
106 + .name = PBS_OPT_KEYFILE,
107 + .type = QEMU_OPT_STRING,
108 + .help = "Optional: The path to the keyfile to use.",
109 + },
110 + { /* end of list */ }
111 + },
112 +};
113 +
114 +
115 +// filename format:
116 +// pbs:repository=<repo>,snapshot=<snap>,password=<pw>,key_password=<kpw>,fingerprint=<fp>,archive=<archive>
117 +static void pbs_parse_filename(const char *filename, QDict *options,
118 + Error **errp)
119 +{
120 +
121 + if (!strstart(filename, "pbs:", &filename)) {
122 + if (errp) error_setg(errp, "pbs_parse_filename failed - missing 'pbs:' prefix");
123 + }
124 +
125 +
126 + QemuOpts *opts = qemu_opts_parse_noisily(&runtime_opts, filename, false);
127 + if (!opts) {
128 + if (errp) error_setg(errp, "pbs_parse_filename failed");
129 + return;
130 + }
131 +
132 + qemu_opts_to_qdict(opts, options);
133 +
134 + qemu_opts_del(opts);
135 +}
136 +
137 +static int pbs_open(BlockDriverState *bs, QDict *options, int flags,
138 + Error **errp)
139 +{
140 + QemuOpts *opts;
141 + BDRVPBSState *s = bs->opaque;
142 + char *pbs_error = NULL;
143 +
144 + opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
145 + qemu_opts_absorb_qdict(opts, options, &error_abort);
146 +
147 + s->repository = g_strdup(qemu_opt_get(opts, PBS_OPT_REPOSITORY));
148 + s->snapshot = g_strdup(qemu_opt_get(opts, PBS_OPT_SNAPSHOT));
149 + s->archive = g_strdup(qemu_opt_get(opts, PBS_OPT_ARCHIVE));
150 + const char *keyfile = qemu_opt_get(opts, PBS_OPT_KEYFILE);
151 + const char *password = qemu_opt_get(opts, PBS_OPT_PASSWORD);
152 + const char *fingerprint = qemu_opt_get(opts, PBS_OPT_FINGERPRINT);
153 + const char *key_password = qemu_opt_get(opts, PBS_OPT_ENCRYPTION_PASSWORD);
154 +
155 + if (!password) {
156 + password = getenv("PBS_PASSWORD");
157 + }
158 + if (!fingerprint) {
159 + fingerprint = getenv("PBS_FINGERPRINT");
160 + }
161 + if (!key_password) {
162 + key_password = getenv("PBS_ENCRYPTION_PASSWORD");
163 + }
164 +
165 + /* connect to PBS server in read mode */
166 + s->conn = proxmox_restore_new(s->repository, s->snapshot, password,
167 + keyfile, key_password, fingerprint, &pbs_error);
168 +
169 + /* invalidates qemu_opt_get char pointers from above */
170 + qemu_opts_del(opts);
171 +
172 + if (!s->conn) {
173 + if (pbs_error && errp) error_setg(errp, "PBS restore_new failed: %s", pbs_error);
174 + if (pbs_error) proxmox_backup_free_error(pbs_error);
175 + return -ENOMEM;
176 + }
177 +
178 + int ret = proxmox_restore_connect(s->conn, &pbs_error);
179 + if (ret < 0) {
180 + if (pbs_error && errp) error_setg(errp, "PBS connect failed: %s", pbs_error);
181 + if (pbs_error) proxmox_backup_free_error(pbs_error);
182 + return -ECONNREFUSED;
183 + }
184 +
185 + /* acquire handle and length */
186 + s->aid = proxmox_restore_open_image(s->conn, s->archive, &pbs_error);
187 + if (s->aid < 0) {
188 + if (pbs_error && errp) error_setg(errp, "PBS open_image failed: %s", pbs_error);
189 + if (pbs_error) proxmox_backup_free_error(pbs_error);
190 + return -ENODEV;
191 + }
192 + s->length = proxmox_restore_get_image_length(s->conn, s->aid, &pbs_error);
193 + if (s->length < 0) {
194 + if (pbs_error && errp) error_setg(errp, "PBS get_image_length failed: %s", pbs_error);
195 + if (pbs_error) proxmox_backup_free_error(pbs_error);
196 + return -EINVAL;
197 + }
198 +
199 + return 0;
200 +}
201 +
202 +static int pbs_file_open(BlockDriverState *bs, QDict *options, int flags,
203 + Error **errp)
204 +{
205 + return pbs_open(bs, options, flags, errp);
206 +}
207 +
208 +static void pbs_close(BlockDriverState *bs) {
209 + BDRVPBSState *s = bs->opaque;
210 + g_free(s->repository);
211 + g_free(s->snapshot);
212 + g_free(s->archive);
213 + proxmox_restore_disconnect(s->conn);
214 +}
215 +
216 +static int64_t pbs_getlength(BlockDriverState *bs)
217 +{
218 + BDRVPBSState *s = bs->opaque;
219 + return s->length;
220 +}
221 +
222 +typedef struct ReadCallbackData {
223 + Coroutine *co;
224 + AioContext *ctx;
225 +} ReadCallbackData;
226 +
227 +static void read_callback(void *callback_data)
228 +{
229 + ReadCallbackData *rcb = callback_data;
230 + aio_co_schedule(rcb->ctx, rcb->co);
231 +}
232 +
233 +static coroutine_fn int pbs_co_preadv(BlockDriverState *bs,
234 + uint64_t offset, uint64_t bytes,
235 + QEMUIOVector *qiov, int flags)
236 +{
237 + BDRVPBSState *s = bs->opaque;
238 + int ret;
239 + char *pbs_error = NULL;
240 + uint8_t *buf = malloc(bytes);
241 +
242 + ReadCallbackData rcb = {
243 + .co = qemu_coroutine_self(),
244 + .ctx = qemu_get_current_aio_context(),
245 + };
246 +
247 + proxmox_restore_read_image_at_async(s->conn, s->aid, buf, offset, bytes,
248 + read_callback, (void *) &rcb, &ret, &pbs_error);
249 +
250 + qemu_coroutine_yield();
251 +
252 + if (ret < 0) {
253 + fprintf(stderr, "error during PBS read: %s\n", pbs_error ? pbs_error : "unknown error");
254 + if (pbs_error) proxmox_backup_free_error(pbs_error);
255 + return -EIO;
256 + }
257 +
258 + qemu_iovec_from_buf(qiov, 0, buf, bytes);
259 + free(buf);
260 +
261 + return ret;
262 +}
263 +
264 +static coroutine_fn int pbs_co_pwritev(BlockDriverState *bs,
265 + uint64_t offset, uint64_t bytes,
266 + QEMUIOVector *qiov, int flags)
267 +{
268 + fprintf(stderr, "pbs-bdrv: cannot write to backup file, make sure "
269 + "any attached disk devices are set to read-only!\n");
270 + return -EPERM;
271 +}
272 +
273 +static void pbs_refresh_filename(BlockDriverState *bs)
274 +{
275 + BDRVPBSState *s = bs->opaque;
276 + snprintf(bs->exact_filename, sizeof(bs->exact_filename), "%s/%s(%s)",
277 + s->repository, s->snapshot, s->archive);
278 +}
279 +
280 +static const char *const pbs_strong_runtime_opts[] = {
281 + NULL
282 +};
283 +
284 +static BlockDriver bdrv_pbs_co = {
285 + .format_name = "pbs",
286 + .protocol_name = "pbs",
287 + .instance_size = sizeof(BDRVPBSState),
288 +
289 + .bdrv_parse_filename = pbs_parse_filename,
290 +
291 + .bdrv_file_open = pbs_file_open,
292 + .bdrv_open = pbs_open,
293 + .bdrv_close = pbs_close,
294 + .bdrv_getlength = pbs_getlength,
295 +
296 + .bdrv_co_preadv = pbs_co_preadv,
297 + .bdrv_co_pwritev = pbs_co_pwritev,
298 +
299 + .bdrv_refresh_filename = pbs_refresh_filename,
300 + .strong_runtime_opts = pbs_strong_runtime_opts,
301 +};
302 +
303 +static void bdrv_pbs_init(void)
304 +{
305 + bdrv_register(&bdrv_pbs_co);
306 +}
307 +
308 +block_init(bdrv_pbs_init);
309 diff --git a/configure b/configure
310 index 18c26e0389..33d9933871 100755
311 --- a/configure
312 +++ b/configure
313 @@ -436,6 +436,7 @@ vvfat="yes"
314 qed="yes"
315 parallels="yes"
316 sheepdog="no"
317 +pbs_bdrv="yes"
318 libxml2=""
319 debug_mutex="no"
320 libpmem=""
321 @@ -1461,6 +1462,10 @@ for opt do
322 ;;
323 --enable-sheepdog) sheepdog="yes"
324 ;;
325 + --disable-pbs-bdrv) pbs_bdrv="no"
326 + ;;
327 + --enable-pbs-bdrv) pbs_bdrv="yes"
328 + ;;
329 --disable-vhost-user) vhost_user="no"
330 ;;
331 --enable-vhost-user) vhost_user="yes"
332 @@ -1843,6 +1848,7 @@ disabled with --disable-FEATURE, default is enabled if available:
333 qed qed image format support
334 parallels parallels image format support
335 sheepdog sheepdog block driver support (deprecated)
336 + pbs-bdrv Proxmox backup server read-only block driver support
337 crypto-afalg Linux AF_ALG crypto backend driver
338 capstone capstone disassembler support
339 debug-mutex mutex debugging support
340 @@ -6682,6 +6688,9 @@ if test "$sheepdog" = "yes" ; then
341 add_to deprecated_features "sheepdog"
342 echo "CONFIG_SHEEPDOG=y" >> $config_host_mak
343 fi
344 +if test "$pbs_bdrv" = "yes" ; then
345 + echo "CONFIG_PBS_BDRV=y" >> $config_host_mak
346 +fi
347 if test "$pty_h" = "yes" ; then
348 echo "HAVE_PTY_H=y" >> $config_host_mak
349 fi
350 diff --git a/meson.build b/meson.build
351 index 6f1fafee14..4d156d35ce 100644
352 --- a/meson.build
353 +++ b/meson.build
354 @@ -2199,6 +2199,7 @@ summary_info += {'vvfat support': config_host.has_key('CONFIG_VVFAT')}
355 summary_info += {'qed support': config_host.has_key('CONFIG_QED')}
356 summary_info += {'parallels support': config_host.has_key('CONFIG_PARALLELS')}
357 summary_info += {'sheepdog support': config_host.has_key('CONFIG_SHEEPDOG')}
358 +summary_info += {'PBS bdrv support': config_host.has_key('CONFIG_PBS_BDRV')}
359 summary_info += {'capstone': capstone_opt == 'disabled' ? false : capstone_opt}
360 summary_info += {'libpmem support': config_host.has_key('CONFIG_LIBPMEM')}
361 summary_info += {'libdaxctl support': config_host.has_key('CONFIG_LIBDAXCTL')}
362 diff --git a/qapi/block-core.json b/qapi/block-core.json
363 index 0fda1e3fd3..553112d998 100644
364 --- a/qapi/block-core.json
365 +++ b/qapi/block-core.json
366 @@ -2975,7 +2975,7 @@
367 'luks', 'nbd', 'nfs', 'null-aio', 'null-co', 'nvme', 'parallels',
368 'qcow', 'qcow2', 'qed', 'quorum', 'raw', 'rbd',
369 { 'name': 'replication', 'if': 'defined(CONFIG_REPLICATION)' },
370 - 'sheepdog',
371 + 'sheepdog', 'pbs',
372 'ssh', 'throttle', 'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat' ] }
373
374 ##
375 @@ -3039,6 +3039,17 @@
376 { 'struct': 'BlockdevOptionsNull',
377 'data': { '*size': 'int', '*latency-ns': 'uint64', '*read-zeroes': 'bool' } }
378
379 +##
380 +# @BlockdevOptionsPbs:
381 +#
382 +# Driver specific block device options for the PBS backend.
383 +#
384 +##
385 +{ 'struct': 'BlockdevOptionsPbs',
386 + 'data': { 'repository': 'str', 'snapshot': 'str', 'archive': 'str',
387 + '*keyfile': 'str', '*password': 'str', '*fingerprint': 'str',
388 + '*key_password': 'str' } }
389 +
390 ##
391 # @BlockdevOptionsNVMe:
392 #
393 @@ -4148,6 +4159,7 @@
394 'nfs': 'BlockdevOptionsNfs',
395 'null-aio': 'BlockdevOptionsNull',
396 'null-co': 'BlockdevOptionsNull',
397 + 'pbs': 'BlockdevOptionsPbs',
398 'nvme': 'BlockdevOptionsNVMe',
399 'parallels': 'BlockdevOptionsGenericFormat',
400 'qcow2': 'BlockdevOptionsQcow2',