]> git.proxmox.com Git - pve-qemu.git/blame - debian/patches/pve/0032-PVE-Add-PBS-block-driver-to-map-backup-archives-into.patch
update submodule and patches to QEMU 8.1.5
[pve-qemu.git] / debian / patches / pve / 0032-PVE-Add-PBS-block-driver-to-map-backup-archives-into.patch
CommitLineData
3499c5b4
TL
1From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
2From: Stefan Reiter <s.reiter@proxmox.com>
3Date: Wed, 8 Jul 2020 09:50:54 +0200
4Subject: [PATCH] PVE: Add PBS block driver to map backup archives into VMs
5
6Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
7[error cleanups, file_open implementation]
8Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
ddbf7a87 9Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
db5d2a4b
FE
10[WB: add namespace support]
11Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
4e1935c2 12[FE: adapt to changed function signatures
bf251437
FE
13 make pbs_co_preadv return values consistent with QEMU
14 getlength is now a coroutine function]
4e1935c2 15Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
3499c5b4 16---
817b7667 17 block/meson.build | 3 +
db5d2a4b 18 block/pbs.c | 305 +++++++++++++++++++++++++++++++++++++++++++
817b7667 19 configure | 9 ++
5b15e2ec 20 meson.build | 2 +-
4567474e 21 qapi/block-core.json | 13 ++
271ac0a8 22 qapi/pragma.json | 1 +
db5d2a4b 23 6 files changed, 332 insertions(+), 1 deletion(-)
3499c5b4
TL
24 create mode 100644 block/pbs.c
25
817b7667 26diff --git a/block/meson.build b/block/meson.build
10e10933 27index 6d468f89e5..becc99ac4e 100644
817b7667
SR
28--- a/block/meson.build
29+++ b/block/meson.build
10e10933 30@@ -50,6 +50,9 @@ block_ss.add(files(
817b7667
SR
31 '../pve-backup.c',
32 ), libproxmox_backup_qemu)
33
34+block_ss.add(when: 'CONFIG_PBS_BDRV', if_true: files('pbs.c'))
35+block_ss.add(when: 'CONFIG_PBS_BDRV', if_true: libproxmox_backup_qemu)
36+
37
10e10933
FE
38 system_ss.add(when: 'CONFIG_TCG', if_true: files('blkreplay.c'))
39 system_ss.add(files('block-ram-registrar.c'))
3499c5b4
TL
40diff --git a/block/pbs.c b/block/pbs.c
41new file mode 100644
db5d2a4b 42index 0000000000..a2211e0f3b
3499c5b4
TL
43--- /dev/null
44+++ b/block/pbs.c
db5d2a4b 45@@ -0,0 +1,305 @@
3499c5b4
TL
46+/*
47+ * Proxmox Backup Server read-only block driver
48+ */
49+
50+#include "qemu/osdep.h"
51+#include "qapi/error.h"
52+#include "qapi/qmp/qdict.h"
53+#include "qapi/qmp/qstring.h"
54+#include "qemu/module.h"
55+#include "qemu/option.h"
56+#include "qemu/cutils.h"
57+#include "block/block_int.h"
bf251437 58+#include "block/block-io.h"
3499c5b4
TL
59+
60+#include <proxmox-backup-qemu.h>
61+
62+#define PBS_OPT_REPOSITORY "repository"
db5d2a4b 63+#define PBS_OPT_NAMESPACE "namespace"
3499c5b4
TL
64+#define PBS_OPT_SNAPSHOT "snapshot"
65+#define PBS_OPT_ARCHIVE "archive"
66+#define PBS_OPT_KEYFILE "keyfile"
67+#define PBS_OPT_PASSWORD "password"
68+#define PBS_OPT_FINGERPRINT "fingerprint"
69+#define PBS_OPT_ENCRYPTION_PASSWORD "key_password"
70+
71+typedef struct {
72+ ProxmoxRestoreHandle *conn;
73+ char aid;
74+ int64_t length;
75+
76+ char *repository;
db5d2a4b 77+ char *namespace;
3499c5b4
TL
78+ char *snapshot;
79+ char *archive;
80+} BDRVPBSState;
81+
82+static QemuOptsList runtime_opts = {
83+ .name = "pbs",
84+ .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
85+ .desc = {
86+ {
87+ .name = PBS_OPT_REPOSITORY,
88+ .type = QEMU_OPT_STRING,
89+ .help = "The server address and repository to connect to.",
90+ },
91+ {
db5d2a4b
FE
92+ .name = PBS_OPT_NAMESPACE,
93+ .type = QEMU_OPT_STRING,
94+ .help = "Optional: The snapshot's namespace.",
95+ },
96+ {
3499c5b4
TL
97+ .name = PBS_OPT_SNAPSHOT,
98+ .type = QEMU_OPT_STRING,
99+ .help = "The snapshot to read.",
100+ },
101+ {
102+ .name = PBS_OPT_ARCHIVE,
103+ .type = QEMU_OPT_STRING,
104+ .help = "Which archive within the snapshot should be accessed.",
105+ },
106+ {
107+ .name = PBS_OPT_PASSWORD,
108+ .type = QEMU_OPT_STRING,
109+ .help = "Server password. Can be passed as env var 'PBS_PASSWORD'.",
110+ },
111+ {
112+ .name = PBS_OPT_FINGERPRINT,
113+ .type = QEMU_OPT_STRING,
114+ .help = "Server fingerprint. Can be passed as env var 'PBS_FINGERPRINT'.",
115+ },
116+ {
117+ .name = PBS_OPT_ENCRYPTION_PASSWORD,
118+ .type = QEMU_OPT_STRING,
119+ .help = "Optional: Key password. Can be passed as env var 'PBS_ENCRYPTION_PASSWORD'.",
120+ },
121+ {
122+ .name = PBS_OPT_KEYFILE,
123+ .type = QEMU_OPT_STRING,
124+ .help = "Optional: The path to the keyfile to use.",
125+ },
126+ { /* end of list */ }
127+ },
128+};
129+
130+
131+// filename format:
db5d2a4b 132+// pbs:repository=<repo>,namespace=<ns>,snapshot=<snap>,password=<pw>,key_password=<kpw>,fingerprint=<fp>,archive=<archive>
3499c5b4
TL
133+static void pbs_parse_filename(const char *filename, QDict *options,
134+ Error **errp)
135+{
136+
137+ if (!strstart(filename, "pbs:", &filename)) {
138+ if (errp) error_setg(errp, "pbs_parse_filename failed - missing 'pbs:' prefix");
139+ }
140+
141+
142+ QemuOpts *opts = qemu_opts_parse_noisily(&runtime_opts, filename, false);
143+ if (!opts) {
144+ if (errp) error_setg(errp, "pbs_parse_filename failed");
145+ return;
146+ }
147+
148+ qemu_opts_to_qdict(opts, options);
149+
150+ qemu_opts_del(opts);
151+}
152+
153+static int pbs_open(BlockDriverState *bs, QDict *options, int flags,
154+ Error **errp)
155+{
156+ QemuOpts *opts;
157+ BDRVPBSState *s = bs->opaque;
158+ char *pbs_error = NULL;
159+
160+ opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
161+ qemu_opts_absorb_qdict(opts, options, &error_abort);
162+
163+ s->repository = g_strdup(qemu_opt_get(opts, PBS_OPT_REPOSITORY));
164+ s->snapshot = g_strdup(qemu_opt_get(opts, PBS_OPT_SNAPSHOT));
165+ s->archive = g_strdup(qemu_opt_get(opts, PBS_OPT_ARCHIVE));
166+ const char *keyfile = qemu_opt_get(opts, PBS_OPT_KEYFILE);
167+ const char *password = qemu_opt_get(opts, PBS_OPT_PASSWORD);
db5d2a4b 168+ const char *namespace = qemu_opt_get(opts, PBS_OPT_NAMESPACE);
3499c5b4
TL
169+ const char *fingerprint = qemu_opt_get(opts, PBS_OPT_FINGERPRINT);
170+ const char *key_password = qemu_opt_get(opts, PBS_OPT_ENCRYPTION_PASSWORD);
171+
172+ if (!password) {
173+ password = getenv("PBS_PASSWORD");
174+ }
175+ if (!fingerprint) {
176+ fingerprint = getenv("PBS_FINGERPRINT");
177+ }
178+ if (!key_password) {
179+ key_password = getenv("PBS_ENCRYPTION_PASSWORD");
180+ }
db5d2a4b
FE
181+ if (namespace) {
182+ s->namespace = g_strdup(namespace);
183+ }
3499c5b4
TL
184+
185+ /* connect to PBS server in read mode */
db5d2a4b 186+ s->conn = proxmox_restore_new_ns(s->repository, s->snapshot, s->namespace, password,
3499c5b4
TL
187+ keyfile, key_password, fingerprint, &pbs_error);
188+
189+ /* invalidates qemu_opt_get char pointers from above */
190+ qemu_opts_del(opts);
191+
192+ if (!s->conn) {
193+ if (pbs_error && errp) error_setg(errp, "PBS restore_new failed: %s", pbs_error);
194+ if (pbs_error) proxmox_backup_free_error(pbs_error);
195+ return -ENOMEM;
196+ }
197+
198+ int ret = proxmox_restore_connect(s->conn, &pbs_error);
199+ if (ret < 0) {
200+ if (pbs_error && errp) error_setg(errp, "PBS connect failed: %s", pbs_error);
201+ if (pbs_error) proxmox_backup_free_error(pbs_error);
202+ return -ECONNREFUSED;
203+ }
204+
205+ /* acquire handle and length */
206+ s->aid = proxmox_restore_open_image(s->conn, s->archive, &pbs_error);
207+ if (s->aid < 0) {
208+ if (pbs_error && errp) error_setg(errp, "PBS open_image failed: %s", pbs_error);
209+ if (pbs_error) proxmox_backup_free_error(pbs_error);
210+ return -ENODEV;
211+ }
212+ s->length = proxmox_restore_get_image_length(s->conn, s->aid, &pbs_error);
213+ if (s->length < 0) {
214+ if (pbs_error && errp) error_setg(errp, "PBS get_image_length failed: %s", pbs_error);
215+ if (pbs_error) proxmox_backup_free_error(pbs_error);
216+ return -EINVAL;
217+ }
218+
219+ return 0;
220+}
221+
222+static int pbs_file_open(BlockDriverState *bs, QDict *options, int flags,
223+ Error **errp)
224+{
225+ return pbs_open(bs, options, flags, errp);
226+}
227+
228+static void pbs_close(BlockDriverState *bs) {
229+ BDRVPBSState *s = bs->opaque;
230+ g_free(s->repository);
db5d2a4b 231+ g_free(s->namespace);
3499c5b4
TL
232+ g_free(s->snapshot);
233+ g_free(s->archive);
234+ proxmox_restore_disconnect(s->conn);
235+}
236+
bf251437 237+static coroutine_fn int64_t pbs_co_getlength(BlockDriverState *bs)
3499c5b4
TL
238+{
239+ BDRVPBSState *s = bs->opaque;
240+ return s->length;
241+}
242+
243+typedef struct ReadCallbackData {
244+ Coroutine *co;
245+ AioContext *ctx;
246+} ReadCallbackData;
247+
248+static void read_callback(void *callback_data)
249+{
250+ ReadCallbackData *rcb = callback_data;
251+ aio_co_schedule(rcb->ctx, rcb->co);
252+}
253+
254+static coroutine_fn int pbs_co_preadv(BlockDriverState *bs,
4567474e
FE
255+ int64_t offset, int64_t bytes,
256+ QEMUIOVector *qiov, BdrvRequestFlags flags)
3499c5b4
TL
257+{
258+ BDRVPBSState *s = bs->opaque;
259+ int ret;
260+ char *pbs_error = NULL;
db5d2a4b
FE
261+ uint8_t *buf;
262+ bool inline_buf = true;
263+
264+ /* for single-buffer IO vectors we can fast-path the write directly to it */
265+ if (qiov->niov == 1 && qiov->iov->iov_len >= bytes) {
266+ buf = qiov->iov->iov_base;
267+ } else {
268+ inline_buf = false;
269+ buf = g_malloc(bytes);
270+ }
3499c5b4 271+
4567474e
FE
272+ if (offset < 0 || bytes < 0) {
273+ fprintf(stderr, "unexpected negative 'offset' or 'bytes' value!\n");
4e1935c2 274+ return -EIO;
4567474e
FE
275+ }
276+
3499c5b4
TL
277+ ReadCallbackData rcb = {
278+ .co = qemu_coroutine_self(),
76e46478 279+ .ctx = bdrv_get_aio_context(bs),
3499c5b4
TL
280+ };
281+
4567474e 282+ proxmox_restore_read_image_at_async(s->conn, s->aid, buf, (uint64_t)offset, (uint64_t)bytes,
3499c5b4
TL
283+ read_callback, (void *) &rcb, &ret, &pbs_error);
284+
285+ qemu_coroutine_yield();
286+
287+ if (ret < 0) {
288+ fprintf(stderr, "error during PBS read: %s\n", pbs_error ? pbs_error : "unknown error");
289+ if (pbs_error) proxmox_backup_free_error(pbs_error);
290+ return -EIO;
291+ }
292+
db5d2a4b
FE
293+ if (!inline_buf) {
294+ qemu_iovec_from_buf(qiov, 0, buf, bytes);
295+ g_free(buf);
296+ }
3499c5b4 297+
4e1935c2 298+ return 0;
3499c5b4
TL
299+}
300+
301+static coroutine_fn int pbs_co_pwritev(BlockDriverState *bs,
4567474e
FE
302+ int64_t offset, int64_t bytes,
303+ QEMUIOVector *qiov, BdrvRequestFlags flags)
3499c5b4
TL
304+{
305+ fprintf(stderr, "pbs-bdrv: cannot write to backup file, make sure "
306+ "any attached disk devices are set to read-only!\n");
307+ return -EPERM;
308+}
309+
310+static void pbs_refresh_filename(BlockDriverState *bs)
311+{
312+ BDRVPBSState *s = bs->opaque;
db5d2a4b
FE
313+ if (s->namespace) {
314+ snprintf(bs->exact_filename, sizeof(bs->exact_filename), "%s/%s:%s(%s)",
315+ s->repository, s->namespace, s->snapshot, s->archive);
316+ } else {
317+ snprintf(bs->exact_filename, sizeof(bs->exact_filename), "%s/%s(%s)",
318+ s->repository, s->snapshot, s->archive);
319+ }
3499c5b4
TL
320+}
321+
322+static const char *const pbs_strong_runtime_opts[] = {
323+ NULL
324+};
325+
326+static BlockDriver bdrv_pbs_co = {
327+ .format_name = "pbs",
328+ .protocol_name = "pbs",
329+ .instance_size = sizeof(BDRVPBSState),
330+
331+ .bdrv_parse_filename = pbs_parse_filename,
332+
333+ .bdrv_file_open = pbs_file_open,
334+ .bdrv_open = pbs_open,
335+ .bdrv_close = pbs_close,
bf251437 336+ .bdrv_co_getlength = pbs_co_getlength,
3499c5b4
TL
337+
338+ .bdrv_co_preadv = pbs_co_preadv,
339+ .bdrv_co_pwritev = pbs_co_pwritev,
340+
341+ .bdrv_refresh_filename = pbs_refresh_filename,
342+ .strong_runtime_opts = pbs_strong_runtime_opts,
343+};
344+
345+static void bdrv_pbs_init(void)
346+{
347+ bdrv_register(&bdrv_pbs_co);
348+}
349+
350+block_init(bdrv_pbs_init);
351diff --git a/configure b/configure
10e10933 352index 133f4e3235..f5a830c1f3 100755
3499c5b4
TL
353--- a/configure
354+++ b/configure
10e10933
FE
355@@ -256,6 +256,7 @@ qemu_suffix="qemu"
356 softmmu="yes"
357 linux_user=""
5b15e2ec 358 bsd_user=""
3499c5b4 359+pbs_bdrv="yes"
4567474e 360 plugins="$default_feature"
d03e1b3c 361 ninja=""
10e10933
FE
362 python=
363@@ -809,6 +810,10 @@ for opt do
bf251437 364 ;;
10e10933 365 --enable-download) download="enabled"; git_submodules_action=update;
3499c5b4
TL
366 ;;
367+ --disable-pbs-bdrv) pbs_bdrv="no"
368+ ;;
369+ --enable-pbs-bdrv) pbs_bdrv="yes"
370+ ;;
10e10933
FE
371 --enable-plugins) if test "$mingw32" = "yes"; then
372 error_exit "TCG plugins not currently supported on Windows platforms"
373 else
374@@ -959,6 +964,7 @@ cat << EOF
375 bsd-user all BSD usermode emulation targets
376 pie Position Independent Executables
377 debug-tcg TCG debugging (default is disabled)
3499c5b4 378+ pbs-bdrv Proxmox backup server read-only block driver support
dc9827a6
FE
379
380 NOTE: The object files are built at the place where configure is launched
5b15e2ec 381 EOF
10e10933 382@@ -1744,6 +1750,9 @@ if test "$solaris" = "yes" ; then
3499c5b4 383 fi
10e10933
FE
384 echo "SRC_PATH=$source_path" >> $config_host_mak
385 echo "TARGET_DIRS=$target_list" >> $config_host_mak
3499c5b4
TL
386+if test "$pbs_bdrv" = "yes" ; then
387+ echo "CONFIG_PBS_BDRV=y" >> $config_host_mak
388+fi
5b15e2ec
FE
389
390 # XXX: suppress that
391 if [ "$bsd" = "yes" ] ; then
817b7667 392diff --git a/meson.build b/meson.build
10e10933 393index c3330310d9..cbfc9a43fb 100644
817b7667
SR
394--- a/meson.build
395+++ b/meson.build
10e10933 396@@ -4319,7 +4319,7 @@ summary_info += {'bzip2 support': libbzip2}
dc9827a6 397 summary_info += {'lzfse support': liblzfse}
4567474e 398 summary_info += {'zstd support': zstd}
dc9827a6 399 summary_info += {'NUMA host support': numa}
5b15e2ec 400-summary_info += {'capstone': capstone}
817b7667 401+summary_info += {'PBS bdrv support': config_host.has_key('CONFIG_PBS_BDRV')}
4567474e
FE
402 summary_info += {'libpmem support': libpmem}
403 summary_info += {'libdaxctl support': libdaxctl}
5b15e2ec 404 summary_info += {'libudev': libudev}
3499c5b4 405diff --git a/qapi/block-core.json b/qapi/block-core.json
4b7975e7 406index 1b8462a51b..d67a6d448a 100644
3499c5b4
TL
407--- a/qapi/block-core.json
408+++ b/qapi/block-core.json
10e10933 409@@ -3396,6 +3396,7 @@
d03e1b3c
FE
410 'parallels', 'preallocate', 'qcow', 'qcow2', 'qed', 'quorum',
411 'raw', 'rbd',
4567474e 412 { 'name': 'replication', 'if': 'CONFIG_REPLICATION' },
f376b2b9 413+ 'pbs',
d03e1b3c
FE
414 'ssh', 'throttle', 'vdi', 'vhdx',
415 { 'name': 'virtio-blk-vfio-pci', 'if': 'CONFIG_BLKIO' },
416 { 'name': 'virtio-blk-vhost-user', 'if': 'CONFIG_BLKIO' },
10e10933 417@@ -3482,6 +3483,17 @@
3499c5b4
TL
418 { 'struct': 'BlockdevOptionsNull',
419 'data': { '*size': 'int', '*latency-ns': 'uint64', '*read-zeroes': 'bool' } }
420
421+##
422+# @BlockdevOptionsPbs:
423+#
424+# Driver specific block device options for the PBS backend.
425+#
426+##
427+{ 'struct': 'BlockdevOptionsPbs',
428+ 'data': { 'repository': 'str', 'snapshot': 'str', 'archive': 'str',
429+ '*keyfile': 'str', '*password': 'str', '*fingerprint': 'str',
db5d2a4b 430+ '*key_password': 'str', '*namespace': 'str' } }
3499c5b4
TL
431+
432 ##
433 # @BlockdevOptionsNVMe:
434 #
4b7975e7 435@@ -4890,6 +4902,7 @@
3499c5b4
TL
436 'nfs': 'BlockdevOptionsNfs',
437 'null-aio': 'BlockdevOptionsNull',
438 'null-co': 'BlockdevOptionsNull',
439+ 'pbs': 'BlockdevOptionsPbs',
440 'nvme': 'BlockdevOptionsNVMe',
d03e1b3c
FE
441 'nvme-io_uring': { 'type': 'BlockdevOptionsNvmeIoUring',
442 'if': 'CONFIG_BLKIO' },
271ac0a8 443diff --git a/qapi/pragma.json b/qapi/pragma.json
a816d296 444index 325e684411..b6079f6a0e 100644
271ac0a8
FE
445--- a/qapi/pragma.json
446+++ b/qapi/pragma.json
a816d296 447@@ -45,6 +45,7 @@
271ac0a8
FE
448 'BlockInfo', # query-block
449 'BlockdevAioOptions', # blockdev-add, -blockdev
450 'BlockdevDriver', # blockdev-add, query-blockstats, ...
451+ 'BlockdevOptionsPbs', # for PBS backwards compat
452 'BlockdevVmdkAdapterType', # blockdev-create (to match VMDK spec)
453 'BlockdevVmdkSubformat', # blockdev-create (to match VMDK spec)
454 'ColoCompareProperties', # object_add, -object