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