]> git.proxmox.com Git - pve-qemu.git/blame - debian/patches/pve/0033-PVE-Add-PBS-block-driver-to-map-backup-archives-into.patch
update submodule and patches to 7.2.0
[pve-qemu.git] / debian / patches / pve / 0033-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>
4e1935c2
FE
10[FE: adapt to changed function signatures
11 make pbs_co_preadv return values consistent with QEMU]
12Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
3499c5b4 13---
817b7667 14 block/meson.build | 3 +
4567474e 15 block/pbs.c | 276 +++++++++++++++++++++++++++++++++++++++++++
817b7667 16 configure | 9 ++
5b15e2ec 17 meson.build | 2 +-
4567474e 18 qapi/block-core.json | 13 ++
5b15e2ec 19 5 files changed, 302 insertions(+), 1 deletion(-)
3499c5b4
TL
20 create mode 100644 block/pbs.c
21
817b7667 22diff --git a/block/meson.build b/block/meson.build
d03e1b3c 23index e995ae72b9..7ef2fa72d5 100644
817b7667
SR
24--- a/block/meson.build
25+++ b/block/meson.build
dc9827a6 26@@ -53,6 +53,9 @@ block_ss.add(files(
817b7667
SR
27 '../pve-backup.c',
28 ), libproxmox_backup_qemu)
29
30+block_ss.add(when: 'CONFIG_PBS_BDRV', if_true: files('pbs.c'))
31+block_ss.add(when: 'CONFIG_PBS_BDRV', if_true: libproxmox_backup_qemu)
32+
33
34 softmmu_ss.add(when: 'CONFIG_TCG', if_true: files('blkreplay.c'))
d03e1b3c 35 softmmu_ss.add(files('block-ram-registrar.c'))
3499c5b4
TL
36diff --git a/block/pbs.c b/block/pbs.c
37new file mode 100644
4e1935c2 38index 0000000000..9d1f1f39d4
3499c5b4
TL
39--- /dev/null
40+++ b/block/pbs.c
4567474e 41@@ -0,0 +1,276 @@
3499c5b4
TL
42+/*
43+ * Proxmox Backup Server read-only block driver
44+ */
45+
46+#include "qemu/osdep.h"
47+#include "qapi/error.h"
48+#include "qapi/qmp/qdict.h"
49+#include "qapi/qmp/qstring.h"
50+#include "qemu/module.h"
51+#include "qemu/option.h"
52+#include "qemu/cutils.h"
53+#include "block/block_int.h"
54+
55+#include <proxmox-backup-qemu.h>
56+
57+#define PBS_OPT_REPOSITORY "repository"
58+#define PBS_OPT_SNAPSHOT "snapshot"
59+#define PBS_OPT_ARCHIVE "archive"
60+#define PBS_OPT_KEYFILE "keyfile"
61+#define PBS_OPT_PASSWORD "password"
62+#define PBS_OPT_FINGERPRINT "fingerprint"
63+#define PBS_OPT_ENCRYPTION_PASSWORD "key_password"
64+
65+typedef struct {
66+ ProxmoxRestoreHandle *conn;
67+ char aid;
68+ int64_t length;
69+
70+ char *repository;
71+ char *snapshot;
72+ char *archive;
73+} BDRVPBSState;
74+
75+static QemuOptsList runtime_opts = {
76+ .name = "pbs",
77+ .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
78+ .desc = {
79+ {
80+ .name = PBS_OPT_REPOSITORY,
81+ .type = QEMU_OPT_STRING,
82+ .help = "The server address and repository to connect to.",
83+ },
84+ {
85+ .name = PBS_OPT_SNAPSHOT,
86+ .type = QEMU_OPT_STRING,
87+ .help = "The snapshot to read.",
88+ },
89+ {
90+ .name = PBS_OPT_ARCHIVE,
91+ .type = QEMU_OPT_STRING,
92+ .help = "Which archive within the snapshot should be accessed.",
93+ },
94+ {
95+ .name = PBS_OPT_PASSWORD,
96+ .type = QEMU_OPT_STRING,
97+ .help = "Server password. Can be passed as env var 'PBS_PASSWORD'.",
98+ },
99+ {
100+ .name = PBS_OPT_FINGERPRINT,
101+ .type = QEMU_OPT_STRING,
102+ .help = "Server fingerprint. Can be passed as env var 'PBS_FINGERPRINT'.",
103+ },
104+ {
105+ .name = PBS_OPT_ENCRYPTION_PASSWORD,
106+ .type = QEMU_OPT_STRING,
107+ .help = "Optional: Key password. Can be passed as env var 'PBS_ENCRYPTION_PASSWORD'.",
108+ },
109+ {
110+ .name = PBS_OPT_KEYFILE,
111+ .type = QEMU_OPT_STRING,
112+ .help = "Optional: The path to the keyfile to use.",
113+ },
114+ { /* end of list */ }
115+ },
116+};
117+
118+
119+// filename format:
120+// pbs:repository=<repo>,snapshot=<snap>,password=<pw>,key_password=<kpw>,fingerprint=<fp>,archive=<archive>
121+static void pbs_parse_filename(const char *filename, QDict *options,
122+ Error **errp)
123+{
124+
125+ if (!strstart(filename, "pbs:", &filename)) {
126+ if (errp) error_setg(errp, "pbs_parse_filename failed - missing 'pbs:' prefix");
127+ }
128+
129+
130+ QemuOpts *opts = qemu_opts_parse_noisily(&runtime_opts, filename, false);
131+ if (!opts) {
132+ if (errp) error_setg(errp, "pbs_parse_filename failed");
133+ return;
134+ }
135+
136+ qemu_opts_to_qdict(opts, options);
137+
138+ qemu_opts_del(opts);
139+}
140+
141+static int pbs_open(BlockDriverState *bs, QDict *options, int flags,
142+ Error **errp)
143+{
144+ QemuOpts *opts;
145+ BDRVPBSState *s = bs->opaque;
146+ char *pbs_error = NULL;
147+
148+ opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
149+ qemu_opts_absorb_qdict(opts, options, &error_abort);
150+
151+ s->repository = g_strdup(qemu_opt_get(opts, PBS_OPT_REPOSITORY));
152+ s->snapshot = g_strdup(qemu_opt_get(opts, PBS_OPT_SNAPSHOT));
153+ s->archive = g_strdup(qemu_opt_get(opts, PBS_OPT_ARCHIVE));
154+ const char *keyfile = qemu_opt_get(opts, PBS_OPT_KEYFILE);
155+ const char *password = qemu_opt_get(opts, PBS_OPT_PASSWORD);
156+ const char *fingerprint = qemu_opt_get(opts, PBS_OPT_FINGERPRINT);
157+ const char *key_password = qemu_opt_get(opts, PBS_OPT_ENCRYPTION_PASSWORD);
158+
159+ if (!password) {
160+ password = getenv("PBS_PASSWORD");
161+ }
162+ if (!fingerprint) {
163+ fingerprint = getenv("PBS_FINGERPRINT");
164+ }
165+ if (!key_password) {
166+ key_password = getenv("PBS_ENCRYPTION_PASSWORD");
167+ }
168+
169+ /* connect to PBS server in read mode */
170+ s->conn = proxmox_restore_new(s->repository, s->snapshot, password,
171+ keyfile, key_password, fingerprint, &pbs_error);
172+
173+ /* invalidates qemu_opt_get char pointers from above */
174+ qemu_opts_del(opts);
175+
176+ if (!s->conn) {
177+ if (pbs_error && errp) error_setg(errp, "PBS restore_new failed: %s", pbs_error);
178+ if (pbs_error) proxmox_backup_free_error(pbs_error);
179+ return -ENOMEM;
180+ }
181+
182+ int ret = proxmox_restore_connect(s->conn, &pbs_error);
183+ if (ret < 0) {
184+ if (pbs_error && errp) error_setg(errp, "PBS connect failed: %s", pbs_error);
185+ if (pbs_error) proxmox_backup_free_error(pbs_error);
186+ return -ECONNREFUSED;
187+ }
188+
189+ /* acquire handle and length */
190+ s->aid = proxmox_restore_open_image(s->conn, s->archive, &pbs_error);
191+ if (s->aid < 0) {
192+ if (pbs_error && errp) error_setg(errp, "PBS open_image failed: %s", pbs_error);
193+ if (pbs_error) proxmox_backup_free_error(pbs_error);
194+ return -ENODEV;
195+ }
196+ s->length = proxmox_restore_get_image_length(s->conn, s->aid, &pbs_error);
197+ if (s->length < 0) {
198+ if (pbs_error && errp) error_setg(errp, "PBS get_image_length failed: %s", pbs_error);
199+ if (pbs_error) proxmox_backup_free_error(pbs_error);
200+ return -EINVAL;
201+ }
202+
203+ return 0;
204+}
205+
206+static int pbs_file_open(BlockDriverState *bs, QDict *options, int flags,
207+ Error **errp)
208+{
209+ return pbs_open(bs, options, flags, errp);
210+}
211+
212+static void pbs_close(BlockDriverState *bs) {
213+ BDRVPBSState *s = bs->opaque;
214+ g_free(s->repository);
215+ g_free(s->snapshot);
216+ g_free(s->archive);
217+ proxmox_restore_disconnect(s->conn);
218+}
219+
220+static int64_t pbs_getlength(BlockDriverState *bs)
221+{
222+ BDRVPBSState *s = bs->opaque;
223+ return s->length;
224+}
225+
226+typedef struct ReadCallbackData {
227+ Coroutine *co;
228+ AioContext *ctx;
229+} ReadCallbackData;
230+
231+static void read_callback(void *callback_data)
232+{
233+ ReadCallbackData *rcb = callback_data;
234+ aio_co_schedule(rcb->ctx, rcb->co);
235+}
236+
237+static coroutine_fn int pbs_co_preadv(BlockDriverState *bs,
4567474e
FE
238+ int64_t offset, int64_t bytes,
239+ QEMUIOVector *qiov, BdrvRequestFlags flags)
3499c5b4
TL
240+{
241+ BDRVPBSState *s = bs->opaque;
242+ int ret;
243+ char *pbs_error = NULL;
244+ uint8_t *buf = malloc(bytes);
245+
4567474e
FE
246+ if (offset < 0 || bytes < 0) {
247+ fprintf(stderr, "unexpected negative 'offset' or 'bytes' value!\n");
4e1935c2 248+ return -EIO;
4567474e
FE
249+ }
250+
3499c5b4
TL
251+ ReadCallbackData rcb = {
252+ .co = qemu_coroutine_self(),
76e46478 253+ .ctx = bdrv_get_aio_context(bs),
3499c5b4
TL
254+ };
255+
4567474e 256+ proxmox_restore_read_image_at_async(s->conn, s->aid, buf, (uint64_t)offset, (uint64_t)bytes,
3499c5b4
TL
257+ read_callback, (void *) &rcb, &ret, &pbs_error);
258+
259+ qemu_coroutine_yield();
260+
261+ if (ret < 0) {
262+ fprintf(stderr, "error during PBS read: %s\n", pbs_error ? pbs_error : "unknown error");
263+ if (pbs_error) proxmox_backup_free_error(pbs_error);
264+ return -EIO;
265+ }
266+
267+ qemu_iovec_from_buf(qiov, 0, buf, bytes);
268+ free(buf);
269+
4e1935c2 270+ return 0;
3499c5b4
TL
271+}
272+
273+static coroutine_fn int pbs_co_pwritev(BlockDriverState *bs,
4567474e
FE
274+ int64_t offset, int64_t bytes,
275+ QEMUIOVector *qiov, BdrvRequestFlags flags)
3499c5b4
TL
276+{
277+ fprintf(stderr, "pbs-bdrv: cannot write to backup file, make sure "
278+ "any attached disk devices are set to read-only!\n");
279+ return -EPERM;
280+}
281+
282+static void pbs_refresh_filename(BlockDriverState *bs)
283+{
284+ BDRVPBSState *s = bs->opaque;
285+ snprintf(bs->exact_filename, sizeof(bs->exact_filename), "%s/%s(%s)",
286+ s->repository, s->snapshot, s->archive);
287+}
288+
289+static const char *const pbs_strong_runtime_opts[] = {
290+ NULL
291+};
292+
293+static BlockDriver bdrv_pbs_co = {
294+ .format_name = "pbs",
295+ .protocol_name = "pbs",
296+ .instance_size = sizeof(BDRVPBSState),
297+
298+ .bdrv_parse_filename = pbs_parse_filename,
299+
300+ .bdrv_file_open = pbs_file_open,
301+ .bdrv_open = pbs_open,
302+ .bdrv_close = pbs_close,
303+ .bdrv_getlength = pbs_getlength,
304+
305+ .bdrv_co_preadv = pbs_co_preadv,
306+ .bdrv_co_pwritev = pbs_co_pwritev,
307+
308+ .bdrv_refresh_filename = pbs_refresh_filename,
309+ .strong_runtime_opts = pbs_strong_runtime_opts,
310+};
311+
312+static void bdrv_pbs_init(void)
313+{
314+ bdrv_register(&bdrv_pbs_co);
315+}
316+
317+block_init(bdrv_pbs_init);
318diff --git a/configure b/configure
d03e1b3c 319index 26c7bc5154..c587e986c7 100755
3499c5b4
TL
320--- a/configure
321+++ b/configure
d03e1b3c 322@@ -285,6 +285,7 @@ linux_user=""
5b15e2ec
FE
323 bsd_user=""
324 pie=""
dc9827a6 325 coroutine=""
3499c5b4 326+pbs_bdrv="yes"
4567474e 327 plugins="$default_feature"
dc9827a6 328 meson=""
d03e1b3c
FE
329 ninja=""
330@@ -864,6 +865,10 @@ for opt do
5b15e2ec
FE
331 --enable-uuid|--disable-uuid)
332 echo "$0: $opt is obsolete, UUID support is always built" >&2
3499c5b4
TL
333 ;;
334+ --disable-pbs-bdrv) pbs_bdrv="no"
335+ ;;
336+ --enable-pbs-bdrv) pbs_bdrv="yes"
337+ ;;
5b15e2ec 338 --with-git=*) git="$optarg"
3499c5b4 339 ;;
5b15e2ec 340 --with-git-submodules=*)
d03e1b3c 341@@ -1049,6 +1054,7 @@ cat << EOF
5b15e2ec
FE
342 debug-info debugging information
343 safe-stack SafeStack Stack Smash Protection. Depends on
344 clang/llvm >= 3.7 and requires coroutine backend ucontext.
3499c5b4 345+ pbs-bdrv Proxmox backup server read-only block driver support
dc9827a6
FE
346
347 NOTE: The object files are built at the place where configure is launched
5b15e2ec 348 EOF
d03e1b3c 349@@ -2372,6 +2378,9 @@ echo "TARGET_DIRS=$target_list" >> $config_host_mak
5b15e2ec
FE
350 if test "$modules" = "yes"; then
351 echo "CONFIG_MODULES=y" >> $config_host_mak
3499c5b4
TL
352 fi
353+if test "$pbs_bdrv" = "yes" ; then
354+ echo "CONFIG_PBS_BDRV=y" >> $config_host_mak
355+fi
5b15e2ec
FE
356
357 # XXX: suppress that
358 if [ "$bsd" = "yes" ] ; then
817b7667 359diff --git a/meson.build b/meson.build
d03e1b3c 360index 63ea813a9a..f7f5b3f253 100644
817b7667
SR
361--- a/meson.build
362+++ b/meson.build
d03e1b3c 363@@ -3978,7 +3978,7 @@ summary_info += {'bzip2 support': libbzip2}
dc9827a6 364 summary_info += {'lzfse support': liblzfse}
4567474e 365 summary_info += {'zstd support': zstd}
dc9827a6 366 summary_info += {'NUMA host support': numa}
5b15e2ec 367-summary_info += {'capstone': capstone}
817b7667 368+summary_info += {'PBS bdrv support': config_host.has_key('CONFIG_PBS_BDRV')}
4567474e
FE
369 summary_info += {'libpmem support': libpmem}
370 summary_info += {'libdaxctl support': libdaxctl}
5b15e2ec 371 summary_info += {'libudev': libudev}
3499c5b4 372diff --git a/qapi/block-core.json b/qapi/block-core.json
d03e1b3c 373index 5ac6276dc1..45b63dfe26 100644
3499c5b4
TL
374--- a/qapi/block-core.json
375+++ b/qapi/block-core.json
d03e1b3c
FE
376@@ -3103,6 +3103,7 @@
377 'parallels', 'preallocate', 'qcow', 'qcow2', 'qed', 'quorum',
378 'raw', 'rbd',
4567474e 379 { 'name': 'replication', 'if': 'CONFIG_REPLICATION' },
f376b2b9 380+ 'pbs',
d03e1b3c
FE
381 'ssh', 'throttle', 'vdi', 'vhdx',
382 { 'name': 'virtio-blk-vfio-pci', 'if': 'CONFIG_BLKIO' },
383 { 'name': 'virtio-blk-vhost-user', 'if': 'CONFIG_BLKIO' },
384@@ -3179,6 +3180,17 @@
3499c5b4
TL
385 { 'struct': 'BlockdevOptionsNull',
386 'data': { '*size': 'int', '*latency-ns': 'uint64', '*read-zeroes': 'bool' } }
387
388+##
389+# @BlockdevOptionsPbs:
390+#
391+# Driver specific block device options for the PBS backend.
392+#
393+##
394+{ 'struct': 'BlockdevOptionsPbs',
395+ 'data': { 'repository': 'str', 'snapshot': 'str', 'archive': 'str',
396+ '*keyfile': 'str', '*password': 'str', '*fingerprint': 'str',
397+ '*key_password': 'str' } }
398+
399 ##
400 # @BlockdevOptionsNVMe:
401 #
d03e1b3c 402@@ -4531,6 +4543,7 @@
3499c5b4
TL
403 'nfs': 'BlockdevOptionsNfs',
404 'null-aio': 'BlockdevOptionsNull',
405 'null-co': 'BlockdevOptionsNull',
406+ 'pbs': 'BlockdevOptionsPbs',
407 'nvme': 'BlockdevOptionsNVMe',
d03e1b3c
FE
408 'nvme-io_uring': { 'type': 'BlockdevOptionsNvmeIoUring',
409 'if': 'CONFIG_BLKIO' },