]> git.proxmox.com Git - pve-qemu.git/blame - debian/patches/pve/0031-PVE-Backup-pbs-restore-new-command-to-restore-from-p.patch
backup: improve error when copy-before-write fails for fleecing
[pve-qemu.git] / debian / patches / pve / 0031-PVE-Backup-pbs-restore-new-command-to-restore-from-p.patch
CommitLineData
6402d961
TL
1From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
2From: Dietmar Maurer <dietmar@proxmox.com>
83faa3fe
TL
3Date: Mon, 6 Apr 2020 12:17:01 +0200
4Subject: [PATCH] PVE-Backup: pbs-restore - new command to restore from proxmox
5 backup server
6402d961 6
bce72611 7Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
db5d2a4b
FE
8[WB: add namespace support]
9Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
6402d961 10---
817b7667 11 meson.build | 4 +
db5d2a4b
FE
12 pbs-restore.c | 236 ++++++++++++++++++++++++++++++++++++++++++++++++++
13 2 files changed, 240 insertions(+)
6402d961
TL
14 create mode 100644 pbs-restore.c
15
817b7667 16diff --git a/meson.build b/meson.build
4fbd50e2 17index d16b97cf3c..6de51c34cb 100644
817b7667
SR
18--- a/meson.build
19+++ b/meson.build
4fbd50e2 20@@ -4029,6 +4029,10 @@ if have_tools
c5e8e7c9 21 vma = executable('vma', files('vma.c', 'vma-reader.c') + genh,
817b7667 22 dependencies: [authz, block, crypto, io, qom], install: true)
6402d961 23
c5e8e7c9 24+ pbs_restore = executable('pbs-restore', files('pbs-restore.c') + genh,
817b7667
SR
25+ dependencies: [authz, block, crypto, io, qom,
26+ libproxmox_backup_qemu], install: true)
27+
28 subdir('storage-daemon')
4fbd50e2
FE
29
30 foreach exe: [ 'qemu-img', 'qemu-io', 'qemu-nbd', 'qemu-storage-daemon']
6402d961
TL
31diff --git a/pbs-restore.c b/pbs-restore.c
32new file mode 100644
db5d2a4b 33index 0000000000..f03d9bab8d
6402d961
TL
34--- /dev/null
35+++ b/pbs-restore.c
db5d2a4b 36@@ -0,0 +1,236 @@
6402d961
TL
37+/*
38+ * Qemu image restore helper for Proxmox Backup
39+ *
40+ * Copyright (C) 2019 Proxmox Server Solutions
41+ *
42+ * Authors:
43+ * Dietmar Maurer (dietmar@proxmox.com)
44+ *
45+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
46+ * See the COPYING file in the top-level directory.
47+ *
48+ */
49+
50+#include "qemu/osdep.h"
51+#include <glib.h>
52+#include <getopt.h>
53+#include <string.h>
54+
6402d961
TL
55+#include "qemu/module.h"
56+#include "qemu/error-report.h"
57+#include "qemu/main-loop.h"
58+#include "qemu/cutils.h"
59+#include "qapi/error.h"
60+#include "qapi/qmp/qdict.h"
61+#include "sysemu/block-backend.h"
62+
63+#include <proxmox-backup-qemu.h>
64+
65+static void help(void)
66+{
67+ const char *help_msg =
db5d2a4b 68+ "usage: pbs-restore [--repository <repo>] [--ns namespace] snapshot archive-name target [command options]\n"
6402d961
TL
69+ ;
70+
71+ printf("%s", help_msg);
72+ exit(1);
73+}
74+
75+typedef struct CallbackData {
76+ BlockBackend *target;
77+ uint64_t last_offset;
78+ bool skip_zero;
79+} CallbackData;
80+
81+static int write_callback(
82+ void *callback_data_ptr,
83+ uint64_t offset,
84+ const unsigned char *data,
85+ uint64_t data_len)
86+{
87+ int res = -1;
88+
89+ CallbackData *callback_data = (CallbackData *)callback_data_ptr;
90+
91+ uint64_t last_offset = callback_data->last_offset;
92+ if (offset > last_offset) callback_data->last_offset = offset;
93+
94+ if (data == NULL) {
95+ if (callback_data->skip_zero && offset > last_offset) {
96+ return 0;
97+ }
98+ res = blk_pwrite_zeroes(callback_data->target, offset, data_len, 0);
99+ } else {
5b15e2ec 100+ res = blk_pwrite(callback_data->target, offset, data_len, data, 0);
6402d961
TL
101+ }
102+
103+ if (res < 0) {
104+ fprintf(stderr, "blk_pwrite failed at offset %ld length %ld (%d) - %s\n", offset, data_len, res, strerror(-res));
105+ return res;
106+ }
107+
108+ return 0;
109+}
110+
111+int main(int argc, char **argv)
112+{
113+ Error *main_loop_err = NULL;
114+ const char *format = "raw";
115+ const char *repository = NULL;
db5d2a4b 116+ const char *backup_ns = NULL;
6402d961
TL
117+ const char *keyfile = NULL;
118+ int verbose = false;
119+ bool skip_zero = false;
120+
121+ error_init(argv[0]);
122+
c6979241 123+ for (;;) {
6402d961
TL
124+ static const struct option long_options[] = {
125+ {"help", no_argument, 0, 'h'},
126+ {"skip-zero", no_argument, 0, 'S'},
127+ {"verbose", no_argument, 0, 'v'},
128+ {"format", required_argument, 0, 'f'},
129+ {"repository", required_argument, 0, 'r'},
db5d2a4b 130+ {"ns", required_argument, 0, 'n'},
6402d961
TL
131+ {"keyfile", required_argument, 0, 'k'},
132+ {0, 0, 0, 0}
133+ };
134+ int c = getopt_long(argc, argv, "hvf:r:k:", long_options, NULL);
135+ if (c == -1) {
136+ break;
137+ }
c6979241
TL
138+ switch (c) {
139+ case ':':
140+ fprintf(stderr, "missing argument for option '%s'\n", argv[optind - 1]);
141+ return -1;
142+ case '?':
143+ fprintf(stderr, "unrecognized option '%s'\n", argv[optind - 1]);
144+ return -1;
145+ case 'f':
146+ format = g_strdup(argv[optind - 1]);
147+ break;
148+ case 'r':
149+ repository = g_strdup(argv[optind - 1]);
150+ break;
db5d2a4b
FE
151+ case 'n':
152+ backup_ns = g_strdup(argv[optind - 1]);
153+ break;
c6979241
TL
154+ case 'k':
155+ keyfile = g_strdup(argv[optind - 1]);
156+ break;
157+ case 'v':
158+ verbose = true;
159+ break;
160+ case 'S':
161+ skip_zero = true;
162+ break;
163+ case 'h':
164+ help();
165+ return 0;
6402d961
TL
166+ }
167+ }
168+
169+ if (optind >= argc - 2) {
170+ fprintf(stderr, "missing arguments\n");
171+ help();
172+ return -1;
173+ }
174+
175+ if (repository == NULL) {
176+ repository = getenv("PBS_REPOSITORY");
177+ }
178+
179+ if (repository == NULL) {
180+ fprintf(stderr, "no repository specified\n");
181+ help();
182+ return -1;
183+ }
184+
185+ char *snapshot = argv[optind++];
186+ char *archive_name = argv[optind++];
187+ char *target = argv[optind++];
188+
189+ const char *password = getenv("PBS_PASSWORD");
190+ const char *fingerprint = getenv("PBS_FINGERPRINT");
191+ const char *key_password = getenv("PBS_ENCRYPTION_PASSWORD");
192+
193+ if (qemu_init_main_loop(&main_loop_err)) {
194+ g_error("%s", error_get_pretty(main_loop_err));
195+ }
196+
197+ bdrv_init();
198+ module_call_init(MODULE_INIT_QOM);
199+
bce72611
TL
200+ if (verbose) {
201+ fprintf(stderr, "connecting to repository '%s'\n", repository);
202+ }
6402d961 203+ char *pbs_error = NULL;
db5d2a4b
FE
204+ ProxmoxRestoreHandle *conn = proxmox_restore_new_ns(
205+ repository,
206+ snapshot,
207+ backup_ns,
208+ password,
209+ keyfile,
210+ key_password,
211+ fingerprint,
212+ &pbs_error
213+ );
6402d961
TL
214+ if (conn == NULL) {
215+ fprintf(stderr, "restore failed: %s\n", pbs_error);
216+ return -1;
217+ }
218+
0c893fd8
SR
219+ int res = proxmox_restore_connect(conn, &pbs_error);
220+ if (res < 0 || pbs_error) {
221+ fprintf(stderr, "restore failed (connection error): %s\n", pbs_error);
222+ return -1;
223+ }
224+
6402d961 225+ QDict *options = qdict_new();
6402d961
TL
226+
227+ if (format) {
228+ qdict_put_str(options, "driver", format);
229+ }
230+
bce72611
TL
231+
232+ if (verbose) {
233+ fprintf(stderr, "open block backend for target '%s'\n", target);
234+ }
6402d961
TL
235+ Error *local_err = NULL;
236+ int flags = BDRV_O_RDWR;
6402d961
TL
237+ BlockBackend *blk = blk_new_open(target, NULL, options, flags, &local_err);
238+ if (!blk) {
239+ fprintf(stderr, "%s\n", error_get_pretty(local_err));
240+ return -1;
241+ }
242+
c6979241 243+ CallbackData *callback_data = calloc(sizeof(CallbackData), 1);
6402d961
TL
244+
245+ callback_data->target = blk;
246+ callback_data->skip_zero = skip_zero;
247+ callback_data->last_offset = 0;
248+
249+ // blk_set_enable_write_cache(blk, !writethrough);
250+
bce72611
TL
251+ if (verbose) {
252+ fprintf(stderr, "starting to restore snapshot '%s'\n", snapshot);
fff7e250 253+ fflush(stderr); // ensure we do not get printed after the progress log
bce72611 254+ }
0c893fd8 255+ res = proxmox_restore_image(
6402d961
TL
256+ conn,
257+ archive_name,
258+ write_callback,
259+ callback_data,
260+ &pbs_error,
261+ verbose);
262+
263+ proxmox_restore_disconnect(conn);
41941247 264+ blk_unref(blk);
6402d961
TL
265+
266+ if (res < 0) {
267+ fprintf(stderr, "restore failed: %s\n", pbs_error);
268+ return -1;
269+ }
270+
271+ return 0;
272+}