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