]>
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 TL |
6 | |
7 | --- | |
8 | Makefile | 4 +- | |
c6979241 TL |
9 | pbs-restore.c | 206 ++++++++++++++++++++++++++++++++++++++++++++++++++ |
10 | 2 files changed, 209 insertions(+), 1 deletion(-) | |
6402d961 TL |
11 | create mode 100644 pbs-restore.c |
12 | ||
13 | diff --git a/Makefile b/Makefile | |
b7e851a5 | 14 | index dbd9542ae4..7c1fb58e18 100644 |
6402d961 TL |
15 | --- a/Makefile |
16 | +++ b/Makefile | |
83faa3fe | 17 | @@ -479,7 +479,7 @@ dummy := $(call unnest-vars,, \ |
6402d961 TL |
18 | |
19 | include $(SRC_PATH)/tests/Makefile.include | |
20 | ||
21 | -all: $(DOCS) $(if $(BUILD_DOCS),sphinxdocs) $(TOOLS) vma$(EXESUF) $(HELPERS-y) recurse-all modules $(vhost-user-json-y) | |
22 | +all: $(DOCS) $(if $(BUILD_DOCS),sphinxdocs) $(TOOLS) vma$(EXESUF) pbs-restore$(EXESUF) $(HELPERS-y) recurse-all modules $(vhost-user-json-y) | |
23 | ||
24 | qemu-version.h: FORCE | |
25 | $(call quiet-command, \ | |
83faa3fe TL |
26 | @@ -610,6 +610,8 @@ qemu-io$(EXESUF): qemu-io.o $(authz-obj-y) $(block-obj-y) $(crypto-obj-y) $(io-o |
27 | 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) | |
28 | qemu-storage-daemon$(EXESUF): LIBS += -lproxmox_backup_qemu | |
6402d961 TL |
29 | vma$(EXESUF): vma.o vma-reader.o $(authz-obj-y) $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS) |
30 | +pbs-restore$(EXESUF): pbs-restore.o $(authz-obj-y) $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS) | |
31 | +pbs-restore$(EXESUF): LIBS += -lproxmox_backup_qemu | |
32 | ||
33 | qemu-bridge-helper$(EXESUF): qemu-bridge-helper.o $(COMMON_LDADDS) | |
34 | ||
35 | diff --git a/pbs-restore.c b/pbs-restore.c | |
36 | new file mode 100644 | |
c6979241 | 37 | index 0000000000..0b24e35403 |
6402d961 TL |
38 | --- /dev/null |
39 | +++ b/pbs-restore.c | |
c6979241 | 40 | @@ -0,0 +1,206 @@ |
6402d961 TL |
41 | +/* |
42 | + * Qemu image restore helper for Proxmox Backup | |
43 | + * | |
44 | + * Copyright (C) 2019 Proxmox Server Solutions | |
45 | + * | |
46 | + * Authors: | |
47 | + * Dietmar Maurer (dietmar@proxmox.com) | |
48 | + * | |
49 | + * This work is licensed under the terms of the GNU GPL, version 2 or later. | |
50 | + * See the COPYING file in the top-level directory. | |
51 | + * | |
52 | + */ | |
53 | + | |
54 | +#include "qemu/osdep.h" | |
55 | +#include <glib.h> | |
56 | +#include <getopt.h> | |
57 | +#include <string.h> | |
58 | + | |
59 | +#include "qemu-common.h" | |
60 | +#include "qemu/module.h" | |
61 | +#include "qemu/error-report.h" | |
62 | +#include "qemu/main-loop.h" | |
63 | +#include "qemu/cutils.h" | |
64 | +#include "qapi/error.h" | |
65 | +#include "qapi/qmp/qdict.h" | |
66 | +#include "sysemu/block-backend.h" | |
67 | + | |
68 | +#include <proxmox-backup-qemu.h> | |
69 | + | |
70 | +static void help(void) | |
71 | +{ | |
72 | + const char *help_msg = | |
73 | + "usage: pbs-restore [--repository <repo>] snapshot archive-name target [command options]\n" | |
74 | + ; | |
75 | + | |
76 | + printf("%s", help_msg); | |
77 | + exit(1); | |
78 | +} | |
79 | + | |
80 | +typedef struct CallbackData { | |
81 | + BlockBackend *target; | |
82 | + uint64_t last_offset; | |
83 | + bool skip_zero; | |
84 | +} CallbackData; | |
85 | + | |
86 | +static int write_callback( | |
87 | + void *callback_data_ptr, | |
88 | + uint64_t offset, | |
89 | + const unsigned char *data, | |
90 | + uint64_t data_len) | |
91 | +{ | |
92 | + int res = -1; | |
93 | + | |
94 | + CallbackData *callback_data = (CallbackData *)callback_data_ptr; | |
95 | + | |
96 | + uint64_t last_offset = callback_data->last_offset; | |
97 | + if (offset > last_offset) callback_data->last_offset = offset; | |
98 | + | |
99 | + if (data == NULL) { | |
100 | + if (callback_data->skip_zero && offset > last_offset) { | |
101 | + return 0; | |
102 | + } | |
103 | + res = blk_pwrite_zeroes(callback_data->target, offset, data_len, 0); | |
104 | + } else { | |
105 | + res = blk_pwrite(callback_data->target, offset, data, data_len, 0); | |
106 | + } | |
107 | + | |
108 | + if (res < 0) { | |
109 | + fprintf(stderr, "blk_pwrite failed at offset %ld length %ld (%d) - %s\n", offset, data_len, res, strerror(-res)); | |
110 | + return res; | |
111 | + } | |
112 | + | |
113 | + return 0; | |
114 | +} | |
115 | + | |
116 | +int main(int argc, char **argv) | |
117 | +{ | |
118 | + Error *main_loop_err = NULL; | |
119 | + const char *format = "raw"; | |
120 | + const char *repository = NULL; | |
121 | + const char *keyfile = NULL; | |
122 | + int verbose = false; | |
123 | + bool skip_zero = false; | |
124 | + | |
125 | + error_init(argv[0]); | |
126 | + | |
c6979241 | 127 | + for (;;) { |
6402d961 TL |
128 | + static const struct option long_options[] = { |
129 | + {"help", no_argument, 0, 'h'}, | |
130 | + {"skip-zero", no_argument, 0, 'S'}, | |
131 | + {"verbose", no_argument, 0, 'v'}, | |
132 | + {"format", required_argument, 0, 'f'}, | |
133 | + {"repository", required_argument, 0, 'r'}, | |
134 | + {"keyfile", required_argument, 0, 'k'}, | |
135 | + {0, 0, 0, 0} | |
136 | + }; | |
137 | + int c = getopt_long(argc, argv, "hvf:r:k:", long_options, NULL); | |
138 | + if (c == -1) { | |
139 | + break; | |
140 | + } | |
c6979241 TL |
141 | + switch (c) { |
142 | + case ':': | |
143 | + fprintf(stderr, "missing argument for option '%s'\n", argv[optind - 1]); | |
144 | + return -1; | |
145 | + case '?': | |
146 | + fprintf(stderr, "unrecognized option '%s'\n", argv[optind - 1]); | |
147 | + return -1; | |
148 | + case 'f': | |
149 | + format = g_strdup(argv[optind - 1]); | |
150 | + break; | |
151 | + case 'r': | |
152 | + repository = g_strdup(argv[optind - 1]); | |
153 | + break; | |
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 | + | |
200 | + char *pbs_error = NULL; | |
201 | + ProxmoxRestoreHandle *conn = proxmox_restore_connect( | |
202 | + repository, snapshot, password, keyfile, key_password, fingerprint, &pbs_error); | |
203 | + if (conn == NULL) { | |
204 | + fprintf(stderr, "restore failed: %s\n", pbs_error); | |
205 | + return -1; | |
206 | + } | |
207 | + | |
208 | + QDict *options = qdict_new(); | |
6402d961 TL |
209 | + |
210 | + if (format) { | |
211 | + qdict_put_str(options, "driver", format); | |
212 | + } | |
213 | + | |
214 | + Error *local_err = NULL; | |
215 | + int flags = BDRV_O_RDWR; | |
6402d961 TL |
216 | + BlockBackend *blk = blk_new_open(target, NULL, options, flags, &local_err); |
217 | + if (!blk) { | |
218 | + fprintf(stderr, "%s\n", error_get_pretty(local_err)); | |
219 | + return -1; | |
220 | + } | |
221 | + | |
c6979241 | 222 | + CallbackData *callback_data = calloc(sizeof(CallbackData), 1); |
6402d961 TL |
223 | + |
224 | + callback_data->target = blk; | |
225 | + callback_data->skip_zero = skip_zero; | |
226 | + callback_data->last_offset = 0; | |
227 | + | |
228 | + // blk_set_enable_write_cache(blk, !writethrough); | |
229 | + | |
230 | + int res = proxmox_restore_image( | |
231 | + conn, | |
232 | + archive_name, | |
233 | + write_callback, | |
234 | + callback_data, | |
235 | + &pbs_error, | |
236 | + verbose); | |
237 | + | |
238 | + proxmox_restore_disconnect(conn); | |
239 | + | |
240 | + if (res < 0) { | |
241 | + fprintf(stderr, "restore failed: %s\n", pbs_error); | |
242 | + return -1; | |
243 | + } | |
244 | + | |
245 | + return 0; | |
246 | +} |