]>
Commit | Line | Data |
---|---|---|
6402d961 TL |
1 | From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 |
2 | From: Dietmar Maurer <dietmar@proxmox.com> | |
83faa3fe TL |
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 | |
6402d961 | 6 | |
bce72611 | 7 | Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com> |
6402d961 TL |
8 | --- |
9 | Makefile | 4 +- | |
fff7e250 TL |
10 | pbs-restore.c | 217 ++++++++++++++++++++++++++++++++++++++++++++++++++ |
11 | 2 files changed, 220 insertions(+), 1 deletion(-) | |
6402d961 TL |
12 | create mode 100644 pbs-restore.c |
13 | ||
14 | diff --git a/Makefile b/Makefile | |
60ae3775 | 15 | index aec216968d..b73da29f24 100644 |
6402d961 TL |
16 | --- a/Makefile |
17 | +++ b/Makefile | |
83faa3fe | 18 | @@ -479,7 +479,7 @@ dummy := $(call unnest-vars,, \ |
6402d961 TL |
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, \ | |
60ae3775 | 27 | @@ -604,6 +604,8 @@ qemu-io$(EXESUF): qemu-io.o $(authz-obj-y) $(block-obj-y) $(crypto-obj-y) $(io-o |
83faa3fe TL |
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 | |
6402d961 TL |
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 | |
b570f1c4 | 38 | index 0000000000..4bf37ef1fa |
6402d961 TL |
39 | --- /dev/null |
40 | +++ b/pbs-restore.c | |
fff7e250 | 41 | @@ -0,0 +1,217 @@ |
6402d961 TL |
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 | + | |
c6979241 | 128 | + for (;;) { |
6402d961 TL |
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 | + } | |
c6979241 TL |
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; | |
6402d961 TL |
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 | + | |
bce72611 TL |
201 | + if (verbose) { |
202 | + fprintf(stderr, "connecting to repository '%s'\n", repository); | |
203 | + } | |
6402d961 TL |
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(); | |
6402d961 TL |
213 | + |
214 | + if (format) { | |
215 | + qdict_put_str(options, "driver", format); | |
216 | + } | |
217 | + | |
bce72611 TL |
218 | + |
219 | + if (verbose) { | |
220 | + fprintf(stderr, "open block backend for target '%s'\n", target); | |
221 | + } | |
6402d961 TL |
222 | + Error *local_err = NULL; |
223 | + int flags = BDRV_O_RDWR; | |
6402d961 TL |
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 | + | |
c6979241 | 230 | + CallbackData *callback_data = calloc(sizeof(CallbackData), 1); |
6402d961 TL |
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 | + | |
bce72611 TL |
238 | + if (verbose) { |
239 | + fprintf(stderr, "starting to restore snapshot '%s'\n", snapshot); | |
fff7e250 | 240 | + fflush(stderr); // ensure we do not get printed after the progress log |
bce72611 | 241 | + } |
6402d961 TL |
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 | +} |