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