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
7 Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
8 [WB: add namespace support]
9 Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
12 pbs-restore.c | 236 ++++++++++++++++++++++++++++++++++++++++++++++++++
13 2 files changed, 240 insertions(+)
14 create mode 100644 pbs-restore.c
16 diff --git a/meson.build b/meson.build
17 index d53976d621..c3330310d9 100644
20 @@ -3914,6 +3914,10 @@ if have_tools
21 vma = executable('vma', files('vma.c', 'vma-reader.c') + genh,
22 dependencies: [authz, block, crypto, io, qom], install: true)
24 + pbs_restore = executable('pbs-restore', files('pbs-restore.c') + genh,
25 + dependencies: [authz, block, crypto, io, qom,
26 + libproxmox_backup_qemu], install: true)
28 subdir('storage-daemon')
29 subdir('contrib/rdmacm-mux')
30 subdir('contrib/elf2dmp')
31 diff --git a/pbs-restore.c b/pbs-restore.c
33 index 0000000000..f03d9bab8d
38 + * Qemu image restore helper for Proxmox Backup
40 + * Copyright (C) 2019 Proxmox Server Solutions
43 + * Dietmar Maurer (dietmar@proxmox.com)
45 + * This work is licensed under the terms of the GNU GPL, version 2 or later.
46 + * See the COPYING file in the top-level directory.
50 +#include "qemu/osdep.h"
55 +#include "qemu/module.h"
56 +#include "qemu/error-report.h"
57 +#include "qemu/main-loop.h"
58 +#include "qemu/cutils.h"
59 +#include "qapi/error.h"
60 +#include "qapi/qmp/qdict.h"
61 +#include "sysemu/block-backend.h"
63 +#include <proxmox-backup-qemu.h>
65 +static void help(void)
67 + const char *help_msg =
68 + "usage: pbs-restore [--repository <repo>] [--ns namespace] snapshot archive-name target [command options]\n"
71 + printf("%s", help_msg);
75 +typedef struct CallbackData {
76 + BlockBackend *target;
77 + uint64_t last_offset;
81 +static int write_callback(
82 + void *callback_data_ptr,
84 + const unsigned char *data,
89 + CallbackData *callback_data = (CallbackData *)callback_data_ptr;
91 + uint64_t last_offset = callback_data->last_offset;
92 + if (offset > last_offset) callback_data->last_offset = offset;
95 + if (callback_data->skip_zero && offset > last_offset) {
98 + res = blk_pwrite_zeroes(callback_data->target, offset, data_len, 0);
100 + res = blk_pwrite(callback_data->target, offset, data_len, data, 0);
104 + fprintf(stderr, "blk_pwrite failed at offset %ld length %ld (%d) - %s\n", offset, data_len, res, strerror(-res));
111 +int main(int argc, char **argv)
113 + Error *main_loop_err = NULL;
114 + const char *format = "raw";
115 + const char *repository = NULL;
116 + const char *backup_ns = NULL;
117 + const char *keyfile = NULL;
118 + int verbose = false;
119 + bool skip_zero = false;
121 + error_init(argv[0]);
124 + static const struct option long_options[] = {
125 + {"help", no_argument, 0, 'h'},
126 + {"skip-zero", no_argument, 0, 'S'},
127 + {"verbose", no_argument, 0, 'v'},
128 + {"format", required_argument, 0, 'f'},
129 + {"repository", required_argument, 0, 'r'},
130 + {"ns", required_argument, 0, 'n'},
131 + {"keyfile", required_argument, 0, 'k'},
134 + int c = getopt_long(argc, argv, "hvf:r:k:", long_options, NULL);
140 + fprintf(stderr, "missing argument for option '%s'\n", argv[optind - 1]);
143 + fprintf(stderr, "unrecognized option '%s'\n", argv[optind - 1]);
146 + format = g_strdup(argv[optind - 1]);
149 + repository = g_strdup(argv[optind - 1]);
152 + backup_ns = g_strdup(argv[optind - 1]);
155 + keyfile = g_strdup(argv[optind - 1]);
169 + if (optind >= argc - 2) {
170 + fprintf(stderr, "missing arguments\n");
175 + if (repository == NULL) {
176 + repository = getenv("PBS_REPOSITORY");
179 + if (repository == NULL) {
180 + fprintf(stderr, "no repository specified\n");
185 + char *snapshot = argv[optind++];
186 + char *archive_name = argv[optind++];
187 + char *target = argv[optind++];
189 + const char *password = getenv("PBS_PASSWORD");
190 + const char *fingerprint = getenv("PBS_FINGERPRINT");
191 + const char *key_password = getenv("PBS_ENCRYPTION_PASSWORD");
193 + if (qemu_init_main_loop(&main_loop_err)) {
194 + g_error("%s", error_get_pretty(main_loop_err));
198 + module_call_init(MODULE_INIT_QOM);
201 + fprintf(stderr, "connecting to repository '%s'\n", repository);
203 + char *pbs_error = NULL;
204 + ProxmoxRestoreHandle *conn = proxmox_restore_new_ns(
214 + if (conn == NULL) {
215 + fprintf(stderr, "restore failed: %s\n", pbs_error);
219 + int res = proxmox_restore_connect(conn, &pbs_error);
220 + if (res < 0 || pbs_error) {
221 + fprintf(stderr, "restore failed (connection error): %s\n", pbs_error);
225 + QDict *options = qdict_new();
228 + qdict_put_str(options, "driver", format);
233 + fprintf(stderr, "open block backend for target '%s'\n", target);
235 + Error *local_err = NULL;
236 + int flags = BDRV_O_RDWR;
237 + BlockBackend *blk = blk_new_open(target, NULL, options, flags, &local_err);
239 + fprintf(stderr, "%s\n", error_get_pretty(local_err));
243 + CallbackData *callback_data = calloc(sizeof(CallbackData), 1);
245 + callback_data->target = blk;
246 + callback_data->skip_zero = skip_zero;
247 + callback_data->last_offset = 0;
249 + // blk_set_enable_write_cache(blk, !writethrough);
252 + fprintf(stderr, "starting to restore snapshot '%s'\n", snapshot);
253 + fflush(stderr); // ensure we do not get printed after the progress log
255 + res = proxmox_restore_image(
263 + proxmox_restore_disconnect(conn);
267 + fprintf(stderr, "restore failed: %s\n", pbs_error);