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