refactor: run as root via pkexec, remove sudo handling
- Add privilege escalation at startup (pkexec if not root) - Remove all sudo password handling (echo password | sudo -S) - Remove show_sudo_dialog and current_sudo_password_ - Remove --sudo-password CLI option - All rsync/restore operations run directly as root - format_size: use GB as default unit for large files - Remove [cmd] log leaking sudo password - Simplify snapshot_manager: no sudo_password parameter - Simplify rsync_manager: no sudo_password parameter
This commit is contained in:
+32
-32
@@ -6,6 +6,7 @@
|
|||||||
#include <ctime>
|
#include <ctime>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <unistd.h>
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "rsync_manager.h"
|
#include "rsync_manager.h"
|
||||||
#include "snapshot_manager.h"
|
#include "snapshot_manager.h"
|
||||||
@@ -14,6 +15,27 @@
|
|||||||
static FILE* log_file = nullptr;
|
static FILE* log_file = nullptr;
|
||||||
static bool log_to_stdout = false;
|
static bool log_to_stdout = false;
|
||||||
|
|
||||||
|
static bool is_root() {
|
||||||
|
return getuid() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void escalate_privileges(int argc, char* argv[]) {
|
||||||
|
if (is_root()) return;
|
||||||
|
|
||||||
|
g_print("x3-backup requires root privileges. Re-launching with pkexec...\n");
|
||||||
|
|
||||||
|
std::string cmd = "pkexec";
|
||||||
|
for (int i = 0; i < argc; i++) {
|
||||||
|
std::string arg = argv[i];
|
||||||
|
if (arg.find(' ') != std::string::npos)
|
||||||
|
cmd += " \"" + arg + "\"";
|
||||||
|
else
|
||||||
|
cmd += " " + arg;
|
||||||
|
}
|
||||||
|
int ret = system(cmd.c_str());
|
||||||
|
exit(ret);
|
||||||
|
}
|
||||||
|
|
||||||
static std::string get_log_path() {
|
static std::string get_log_path() {
|
||||||
const char* home = getenv("HOME");
|
const char* home = getenv("HOME");
|
||||||
if (!home) home = ".";
|
if (!home) home = ".";
|
||||||
@@ -66,18 +88,15 @@ static void setup_logging(bool cli_mode) {
|
|||||||
g_log_set_handler("x3-backup", (GLogLevelFlags)(G_LOG_LEVEL_MASK), log_handler, nullptr);
|
g_log_set_handler("x3-backup", (GLogLevelFlags)(G_LOG_LEVEL_MASK), log_handler, nullptr);
|
||||||
g_log_set_handler(nullptr, (GLogLevelFlags)(G_LOG_LEVEL_MASK), log_handler, nullptr);
|
g_log_set_handler(nullptr, (GLogLevelFlags)(G_LOG_LEVEL_MASK), log_handler, nullptr);
|
||||||
|
|
||||||
g_message("=== x3-backup started ===");
|
g_message("=== x3-backup started (uid=%d) ===", getuid());
|
||||||
g_message("Log file: %s", path.c_str());
|
g_message("Log file: %s", path.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
int main(int argc, char* argv[]) {
|
||||||
gtk_init(&argc, &argv);
|
|
||||||
|
|
||||||
bool system_mode = false;
|
bool system_mode = false;
|
||||||
bool backup_now = false;
|
bool backup_now = false;
|
||||||
bool show_help = false;
|
bool show_help = false;
|
||||||
bool cli_mode = false;
|
bool cli_mode = false;
|
||||||
std::string sudo_password;
|
|
||||||
|
|
||||||
for (int i = 1; i < argc; i++) {
|
for (int i = 1; i < argc; i++) {
|
||||||
if (strcmp(argv[i], "--system-backup") == 0) {
|
if (strcmp(argv[i], "--system-backup") == 0) {
|
||||||
@@ -91,29 +110,29 @@ int main(int argc, char* argv[]) {
|
|||||||
show_help = true;
|
show_help = true;
|
||||||
} else if (strcmp(argv[i], "--log") == 0) {
|
} else if (strcmp(argv[i], "--log") == 0) {
|
||||||
log_to_stdout = true;
|
log_to_stdout = true;
|
||||||
} else if (strcmp(argv[i], "--sudo-password") == 0 && i + 1 < argc) {
|
|
||||||
sudo_password = argv[++i];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setup_logging(cli_mode);
|
|
||||||
|
|
||||||
if (show_help) {
|
if (show_help) {
|
||||||
g_print("x3-Backup - GTK3 Backup/Snapshot Tool\n\n");
|
g_print("x3-Backup - GTK3 Backup/Snapshot Tool\n\n");
|
||||||
g_print("Usage: x3-backup [OPTIONS]\n\n");
|
g_print("Usage: x3-backup [OPTIONS]\n\n");
|
||||||
g_print("Options:\n");
|
g_print("Options:\n");
|
||||||
g_print(" --backup Create a backup immediately and exit\n");
|
g_print(" --backup Create a backup immediately and exit\n");
|
||||||
g_print(" --system-backup Create a full system backup and exit\n");
|
g_print(" --system-backup Create a full system backup and exit\n");
|
||||||
g_print(" --sudo-password PW Provide sudo password for backup (required for root permissions)\n");
|
|
||||||
g_print(" --log Print log to stdout\n");
|
g_print(" --log Print log to stdout\n");
|
||||||
g_print(" --help, -h Show this help\n\n");
|
g_print(" --help, -h Show this help\n\n");
|
||||||
g_print("Without options, x3-backup starts in GUI mode with system tray.\n\n");
|
g_print("Without options, x3-backup starts in GUI mode with system tray.\n\n");
|
||||||
g_print("Note: Backups require sudo to preserve file ownership and permissions.\n");
|
g_print("Note: x3-backup requires root privileges and will prompt\n");
|
||||||
g_print(" Use --sudo-password or run interactively for password prompt.\n\n");
|
g_print(" for authentication via pkexec on startup.\n\n");
|
||||||
g_print("Log file: ~/.config/x3-backup/x3-backup.log\n");
|
g_print("Log file: ~/.config/x3-backup/x3-backup.log\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
escalate_privileges(argc, argv);
|
||||||
|
gtk_init(&argc, &argv);
|
||||||
|
|
||||||
|
setup_logging(cli_mode);
|
||||||
|
|
||||||
Config config;
|
Config config;
|
||||||
config.load();
|
config.load();
|
||||||
|
|
||||||
@@ -124,32 +143,13 @@ int main(int argc, char* argv[]) {
|
|||||||
g_message("System backup mode: backing up entire system");
|
g_message("System backup mode: backing up entire system");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Prompt for sudo password if not provided */
|
|
||||||
if (sudo_password.empty()) {
|
|
||||||
g_print("Sudo password required for backup (to preserve file permissions):\n");
|
|
||||||
g_print("Password: ");
|
|
||||||
fflush(stdout);
|
|
||||||
char* pw = nullptr;
|
|
||||||
size_t len = 0;
|
|
||||||
ssize_t nread = getline(&pw, &len, stdin);
|
|
||||||
if (nread > 0 && pw[nread - 1] == '\n') pw[nread - 1] = '\0';
|
|
||||||
if (pw && *pw) {
|
|
||||||
sudo_password = std::string(pw);
|
|
||||||
}
|
|
||||||
free(pw);
|
|
||||||
if (sudo_password.empty()) {
|
|
||||||
g_warning("No password provided, backup may have incorrect permissions");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RsyncManager rsync(config);
|
RsyncManager rsync(config);
|
||||||
SnapshotManager snapshots(config, rsync);
|
SnapshotManager snapshots(config, rsync);
|
||||||
|
|
||||||
g_message("Starting backup with sudo...");
|
g_message("Starting backup...");
|
||||||
snapshots.cleanup_old_snapshots();
|
snapshots.cleanup_old_snapshots();
|
||||||
std::string ts = snapshots.create_snapshot(
|
std::string ts = snapshots.create_snapshot(
|
||||||
system_mode ? "System backup" : "Manual backup",
|
system_mode ? "System backup" : "Manual backup");
|
||||||
sudo_password);
|
|
||||||
if (!ts.empty()) {
|
if (!ts.empty()) {
|
||||||
g_message("Backup completed: %s", ts.c_str());
|
g_message("Backup completed: %s", ts.c_str());
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
+13
-64
@@ -4,16 +4,17 @@
|
|||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <pwd.h>
|
|
||||||
#include <grp.h>
|
|
||||||
|
|
||||||
static std::string format_size(int64_t bytes) {
|
static std::string format_size(int64_t bytes) {
|
||||||
const char* units[] = {"B", "KB", "MB", "GB", "TB"};
|
|
||||||
int u = 0;
|
|
||||||
double size = bytes;
|
|
||||||
while (size >= 1024 && u < 4) { size /= 1024; u++; }
|
|
||||||
char buf[64];
|
char buf[64];
|
||||||
snprintf(buf, sizeof(buf), "%.1f %s", size, units[u]);
|
if (bytes < 1024)
|
||||||
|
snprintf(buf, sizeof(buf), "%ld B", (long)bytes);
|
||||||
|
else if (bytes < 1024LL * 1024)
|
||||||
|
snprintf(buf, sizeof(buf), "%.1f KB", (double)bytes / 1024);
|
||||||
|
else if (bytes < 1024LL * 1024 * 1024)
|
||||||
|
snprintf(buf, sizeof(buf), "%.1f MB", (double)bytes / (1024 * 1024));
|
||||||
|
else
|
||||||
|
snprintf(buf, sizeof(buf), "%.2f GB", (double)bytes / (1024LL * 1024 * 1024));
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -612,11 +613,9 @@ gboolean MainWindow::on_file_button_press(GtkWidget*, GdkEventButton* event, gpo
|
|||||||
auto* ctx = new RestoreCtx{self, file_path};
|
auto* ctx = new RestoreCtx{self, file_path};
|
||||||
g_signal_connect_data(restore_item, "activate", G_CALLBACK(+[](GtkWidget*, gpointer d) {
|
g_signal_connect_data(restore_item, "activate", G_CALLBACK(+[](GtkWidget*, gpointer d) {
|
||||||
auto* c = static_cast<RestoreCtx*>(d);
|
auto* c = static_cast<RestoreCtx*>(d);
|
||||||
c->self->show_sudo_dialog([c](const std::string& pw) {
|
|
||||||
std::vector<std::string> files = {c->path};
|
std::vector<std::string> files = {c->path};
|
||||||
c->self->snapshots_.restore_selected(c->self->current_snapshot_, files, pw);
|
c->self->snapshots_.restore_selected(c->self->current_snapshot_, files);
|
||||||
c->self->tray_.show_notification("x3-Backup", "File restored: " + c->path);
|
c->self->tray_.show_notification("x3-Backup", "File restored: " + c->path);
|
||||||
});
|
|
||||||
delete c;
|
delete c;
|
||||||
}), ctx, nullptr, (GConnectFlags)0);
|
}), ctx, nullptr, (GConnectFlags)0);
|
||||||
gtk_menu_shell_append(GTK_MENU_SHELL(menu), restore_item);
|
gtk_menu_shell_append(GTK_MENU_SHELL(menu), restore_item);
|
||||||
@@ -641,11 +640,9 @@ gboolean MainWindow::on_file_button_press(GtkWidget*, GdkEventButton* event, gpo
|
|||||||
ctx = new RestoreCtx{self, file_path};
|
ctx = new RestoreCtx{self, file_path};
|
||||||
g_signal_connect_data(restore_item, "activate", G_CALLBACK(+[](GtkWidget*, gpointer d) {
|
g_signal_connect_data(restore_item, "activate", G_CALLBACK(+[](GtkWidget*, gpointer d) {
|
||||||
auto* c = static_cast<RestoreCtx*>(d);
|
auto* c = static_cast<RestoreCtx*>(d);
|
||||||
c->self->show_sudo_dialog([c](const std::string& pw) {
|
|
||||||
std::vector<std::string> files = {c->path};
|
std::vector<std::string> files = {c->path};
|
||||||
c->self->snapshots_.restore_selected(c->self->current_snapshot_, files, pw);
|
c->self->snapshots_.restore_selected(c->self->current_snapshot_, files);
|
||||||
c->self->tray_.show_notification("x3-Backup", "Directory restored: " + c->path);
|
c->self->tray_.show_notification("x3-Backup", "Directory restored: " + c->path);
|
||||||
});
|
|
||||||
delete c;
|
delete c;
|
||||||
}), ctx, nullptr, (GConnectFlags)0);
|
}), ctx, nullptr, (GConnectFlags)0);
|
||||||
}
|
}
|
||||||
@@ -658,48 +655,6 @@ gboolean MainWindow::on_file_button_press(GtkWidget*, GdkEventButton* event, gpo
|
|||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::show_sudo_dialog(std::function<void(const std::string&)> callback) {
|
|
||||||
GtkWidget* dialog = gtk_dialog_new_with_buttons(
|
|
||||||
"Authentication Required", GTK_WINDOW(window_),
|
|
||||||
(GtkDialogFlags)(GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT),
|
|
||||||
"_Cancel", GTK_RESPONSE_CANCEL,
|
|
||||||
"_OK", GTK_RESPONSE_ACCEPT,
|
|
||||||
nullptr);
|
|
||||||
|
|
||||||
GtkWidget* content = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
|
|
||||||
GtkWidget* box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 8);
|
|
||||||
gtk_widget_set_margin_start(box, 16);
|
|
||||||
gtk_widget_set_margin_end(box, 16);
|
|
||||||
gtk_widget_set_margin_top(box, 16);
|
|
||||||
|
|
||||||
GtkWidget* icon = gtk_image_new_from_icon_name("dialog-password", GTK_ICON_SIZE_DIALOG);
|
|
||||||
gtk_box_pack_start(GTK_BOX(box), icon, FALSE, FALSE, 0);
|
|
||||||
|
|
||||||
GtkWidget* label = gtk_label_new("Root access is required to preserve file permissions.\n"
|
|
||||||
"Backups will be created with sudo to ensure correct ownership and permissions.\n\n"
|
|
||||||
"Enter your sudo password:");
|
|
||||||
gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
|
|
||||||
gtk_box_pack_start(GTK_BOX(box), label, FALSE, FALSE, 0);
|
|
||||||
|
|
||||||
GtkWidget* pass_entry = gtk_entry_new();
|
|
||||||
gtk_entry_set_visibility(GTK_ENTRY(pass_entry), FALSE);
|
|
||||||
gtk_entry_set_placeholder_text(GTK_ENTRY(pass_entry), "Password");
|
|
||||||
gtk_box_pack_start(GTK_BOX(box), pass_entry, FALSE, FALSE, 0);
|
|
||||||
|
|
||||||
gtk_container_add(GTK_CONTAINER(content), box);
|
|
||||||
gtk_widget_show_all(dialog);
|
|
||||||
gtk_widget_grab_focus(pass_entry);
|
|
||||||
|
|
||||||
if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
|
|
||||||
const char* pw = gtk_entry_get_text(GTK_ENTRY(pass_entry));
|
|
||||||
if (pw && *pw) {
|
|
||||||
current_sudo_password_ = std::string(pw);
|
|
||||||
callback(std::string(pw));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
gtk_widget_destroy(dialog);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainWindow::open_backup_window(const std::string& description) {
|
void MainWindow::open_backup_window(const std::string& description) {
|
||||||
if (backup_win_) {
|
if (backup_win_) {
|
||||||
/* Clear previous output */
|
/* Clear previous output */
|
||||||
@@ -879,17 +834,15 @@ void MainWindow::append_backup_line(const std::string& line, const char* tag) {
|
|||||||
|
|
||||||
void MainWindow::on_backup(GtkWidget*, gpointer d) {
|
void MainWindow::on_backup(GtkWidget*, gpointer d) {
|
||||||
auto* self = static_cast<MainWindow*>(d);
|
auto* self = static_cast<MainWindow*>(d);
|
||||||
self->show_sudo_dialog([self](const std::string& pw) {
|
|
||||||
self->tray_.set_status("x3-Backup - Creating backup...", true);
|
self->tray_.set_status("x3-Backup - Creating backup...", true);
|
||||||
self->open_backup_window("Creating Snapshot...");
|
self->open_backup_window("Creating Snapshot...");
|
||||||
gtk_label_set_text(GTK_LABEL(self->live_log_label_), "Backup running...");
|
gtk_label_set_text(GTK_LABEL(self->live_log_label_), "Backup running...");
|
||||||
|
|
||||||
g_thread_new("backup", [](gpointer data) -> gpointer {
|
g_thread_new("backup", [](gpointer data) -> gpointer {
|
||||||
auto* s = static_cast<MainWindow*>(data);
|
auto* s = static_cast<MainWindow*>(data);
|
||||||
s->snapshots_.create_snapshot("", s->current_sudo_password_);
|
s->snapshots_.create_snapshot("");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}, self);
|
}, self);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::start_quick_backup() {
|
void MainWindow::start_quick_backup() {
|
||||||
@@ -925,13 +878,11 @@ void MainWindow::on_restore(GtkWidget*, gpointer d) {
|
|||||||
? std::string(name) : self->current_path_ + "/" + name;
|
? std::string(name) : self->current_path_ + "/" + name;
|
||||||
g_free(name);
|
g_free(name);
|
||||||
|
|
||||||
self->show_sudo_dialog([self, file_path](const std::string& pw) {
|
|
||||||
std::vector<std::string> files = {file_path};
|
std::vector<std::string> files = {file_path};
|
||||||
self->tray_.set_status("x3-Backup - Restoring...", true);
|
self->tray_.set_status("x3-Backup - Restoring...", true);
|
||||||
self->snapshots_.restore_selected(self->current_snapshot_, files, pw);
|
self->snapshots_.restore_selected(self->current_snapshot_, files);
|
||||||
self->tray_.set_status("x3-Backup - Idle");
|
self->tray_.set_status("x3-Backup - Idle");
|
||||||
self->tray_.show_notification("x3-Backup", "Restored: " + file_path);
|
self->tray_.show_notification("x3-Backup", "Restored: " + file_path);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::on_delete_snapshot(GtkWidget*, gpointer d) {
|
void MainWindow::on_delete_snapshot(GtkWidget*, gpointer d) {
|
||||||
@@ -1053,10 +1004,8 @@ void MainWindow::on_system_restore(GtkWidget*, gpointer d) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
self->show_sudo_dialog([self](const std::string& pw) {
|
self->snapshots_.restore_full(self->current_snapshot_);
|
||||||
self->snapshots_.restore_full(self->current_snapshot_, pw);
|
|
||||||
self->tray_.show_notification("x3-Backup", "Full system restore completed");
|
self->tray_.show_notification("x3-Backup", "Full system restore completed");
|
||||||
});
|
|
||||||
}
|
}
|
||||||
gtk_widget_destroy(dialog);
|
gtk_widget_destroy(dialog);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,6 @@ private:
|
|||||||
void browse_path(const std::string& snapshot, const std::string& path);
|
void browse_path(const std::string& snapshot, const std::string& path);
|
||||||
void navigate_up();
|
void navigate_up();
|
||||||
|
|
||||||
void show_sudo_dialog(std::function<void(const std::string&)> callback);
|
|
||||||
void open_backup_window(const std::string& description = "");
|
void open_backup_window(const std::string& description = "");
|
||||||
void close_backup_window();
|
void close_backup_window();
|
||||||
void append_backup_line(const std::string& line, const char* tag = nullptr);
|
void append_backup_line(const std::string& line, const char* tag = nullptr);
|
||||||
@@ -99,7 +98,6 @@ private:
|
|||||||
|
|
||||||
std::string current_snapshot_;
|
std::string current_snapshot_;
|
||||||
std::string current_path_;
|
std::string current_path_;
|
||||||
std::string current_sudo_password_;
|
|
||||||
std::vector<std::string> path_stack_;
|
std::vector<std::string> path_stack_;
|
||||||
|
|
||||||
int col_name = 0, col_size = 1, col_type = 2, col_permissions = 3, col_mtime = 4;
|
int col_name = 0, col_size = 1, col_type = 2, col_permissions = 3, col_mtime = 4;
|
||||||
|
|||||||
+6
-14
@@ -54,8 +54,7 @@ std::vector<std::string> RsyncManager::build_exclude_args() {
|
|||||||
bool RsyncManager::create_snapshot(const std::string& snapshot_name,
|
bool RsyncManager::create_snapshot(const std::string& snapshot_name,
|
||||||
const std::string& source,
|
const std::string& source,
|
||||||
const std::string& dest,
|
const std::string& dest,
|
||||||
const std::string& link_dest,
|
const std::string& link_dest) {
|
||||||
const std::string& sudo_password) {
|
|
||||||
if (running_) return false;
|
if (running_) return false;
|
||||||
cancel_requested_ = false;
|
cancel_requested_ = false;
|
||||||
running_ = true;
|
running_ = true;
|
||||||
@@ -89,12 +88,7 @@ bool RsyncManager::create_snapshot(const std::string& snapshot_name,
|
|||||||
|
|
||||||
rsync_flags += " \"" + source + "/\" \"" + target + "/\" 2>&1";
|
rsync_flags += " \"" + source + "/\" \"" + target + "/\" 2>&1";
|
||||||
|
|
||||||
std::string cmd;
|
std::string cmd = "mkdir -p \"" + dest + "\" && " + rsync_flags;
|
||||||
if (!sudo_password.empty()) {
|
|
||||||
cmd = "mkdir -p \"" + dest + "\" && echo '" + sudo_password + "' | sudo -S " + rsync_flags;
|
|
||||||
} else {
|
|
||||||
cmd = rsync_flags;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Force line-buffered output so we get real-time progress from rsync */
|
/* Force line-buffered output so we get real-time progress from rsync */
|
||||||
std::string full_cmd = "stdbuf -oL " + cmd;
|
std::string full_cmd = "stdbuf -oL " + cmd;
|
||||||
@@ -201,8 +195,7 @@ bool RsyncManager::create_snapshot(const std::string& snapshot_name,
|
|||||||
|
|
||||||
bool RsyncManager::restore_files(const std::string& snapshot_path,
|
bool RsyncManager::restore_files(const std::string& snapshot_path,
|
||||||
const std::vector<std::string>& files,
|
const std::vector<std::string>& files,
|
||||||
const std::string& restore_dest,
|
const std::string& restore_dest) {
|
||||||
const std::string& sudo_password) {
|
|
||||||
if (running_) return false;
|
if (running_) return false;
|
||||||
cancel_requested_ = false;
|
cancel_requested_ = false;
|
||||||
running_ = true;
|
running_ = true;
|
||||||
@@ -223,7 +216,7 @@ bool RsyncManager::restore_files(const std::string& snapshot_path,
|
|||||||
std::string src = snapshot_path + "/" + f;
|
std::string src = snapshot_path + "/" + f;
|
||||||
std::string dst = restore_dest + "/" + f;
|
std::string dst = restore_dest + "/" + f;
|
||||||
|
|
||||||
std::string cmd = "echo '" + sudo_password + "' | sudo -S rsync -aAX \"" + src + "\" \"" + dst + "\" 2>&1";
|
std::string cmd = "rsync -aAX \"" + src + "\" \"" + dst + "\" 2>&1";
|
||||||
|
|
||||||
g_message("restore cmd: %s", cmd.c_str());
|
g_message("restore cmd: %s", cmd.c_str());
|
||||||
|
|
||||||
@@ -273,8 +266,7 @@ bool RsyncManager::restore_files(const std::string& snapshot_path,
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool RsyncManager::restore_full_snapshot(const std::string& snapshot_path,
|
bool RsyncManager::restore_full_snapshot(const std::string& snapshot_path,
|
||||||
const std::string& restore_dest,
|
const std::string& restore_dest) {
|
||||||
const std::string& sudo_password) {
|
|
||||||
if (running_) return false;
|
if (running_) return false;
|
||||||
cancel_requested_ = false;
|
cancel_requested_ = false;
|
||||||
running_ = true;
|
running_ = true;
|
||||||
@@ -284,7 +276,7 @@ bool RsyncManager::restore_full_snapshot(const std::string& snapshot_path,
|
|||||||
log_buffer_.clear();
|
log_buffer_.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string cmd = "echo '" + sudo_password + "' | sudo -S rsync -aAX --delete \""
|
std::string cmd = "rsync -aAX --delete \""
|
||||||
+ snapshot_path + "/\" \"" + restore_dest + "/\" 2>&1";
|
+ snapshot_path + "/\" \"" + restore_dest + "/\" 2>&1";
|
||||||
|
|
||||||
g_message("full restore cmd: %s", cmd.c_str());
|
g_message("full restore cmd: %s", cmd.c_str());
|
||||||
|
|||||||
+3
-6
@@ -31,17 +31,14 @@ public:
|
|||||||
bool create_snapshot(const std::string& snapshot_name,
|
bool create_snapshot(const std::string& snapshot_name,
|
||||||
const std::string& source,
|
const std::string& source,
|
||||||
const std::string& dest,
|
const std::string& dest,
|
||||||
const std::string& link_dest = "",
|
const std::string& link_dest = "");
|
||||||
const std::string& sudo_password = "");
|
|
||||||
|
|
||||||
bool restore_files(const std::string& snapshot_path,
|
bool restore_files(const std::string& snapshot_path,
|
||||||
const std::vector<std::string>& files,
|
const std::vector<std::string>& files,
|
||||||
const std::string& restore_dest,
|
const std::string& restore_dest);
|
||||||
const std::string& sudo_password);
|
|
||||||
|
|
||||||
bool restore_full_snapshot(const std::string& snapshot_path,
|
bool restore_full_snapshot(const std::string& snapshot_path,
|
||||||
const std::string& restore_dest,
|
const std::string& restore_dest);
|
||||||
const std::string& sudo_password);
|
|
||||||
|
|
||||||
void cancel();
|
void cancel();
|
||||||
bool is_running() const { return running_; }
|
bool is_running() const { return running_; }
|
||||||
|
|||||||
@@ -20,19 +20,12 @@ std::string SnapshotManager::get_timestamp() {
|
|||||||
return std::string(buf);
|
return std::string(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string SnapshotManager::create_snapshot(const std::string& description,
|
std::string SnapshotManager::create_snapshot(const std::string& description) {
|
||||||
const std::string& sudo_password) {
|
|
||||||
std::string ts = get_timestamp();
|
std::string ts = get_timestamp();
|
||||||
std::string snapshots_dir = config_.snapshots_dir();
|
std::string snapshots_dir = config_.snapshots_dir();
|
||||||
std::string metadata_dir = config_.metadata_dir();
|
std::string metadata_dir = config_.metadata_dir();
|
||||||
|
|
||||||
std::string mkdir_cmd;
|
std::string mkdir_cmd = "mkdir -p \"" + snapshots_dir + "\" && mkdir -p \"" + metadata_dir + "\"";
|
||||||
if (!sudo_password.empty()) {
|
|
||||||
mkdir_cmd = "echo '" + sudo_password + "' | sudo -S mkdir -p \"" + snapshots_dir + "\" && "
|
|
||||||
"echo '" + sudo_password + "' | sudo -S mkdir -p \"" + metadata_dir + "\"";
|
|
||||||
} else {
|
|
||||||
mkdir_cmd = "mkdir -p \"" + snapshots_dir + "\" && mkdir -p \"" + metadata_dir + "\"";
|
|
||||||
}
|
|
||||||
system(mkdir_cmd.c_str());
|
system(mkdir_cmd.c_str());
|
||||||
|
|
||||||
/* Find latest snapshot for hardlink delta */
|
/* Find latest snapshot for hardlink delta */
|
||||||
@@ -44,7 +37,7 @@ std::string SnapshotManager::create_snapshot(const std::string& description,
|
|||||||
|
|
||||||
/* Do NOT overwrite rsync_.on_progress — MainWindow already set it */
|
/* Do NOT overwrite rsync_.on_progress — MainWindow already set it */
|
||||||
|
|
||||||
bool ok = rsync_.create_snapshot(ts, config_.settings().source_path, snapshots_dir, link_dest, sudo_password);
|
bool ok = rsync_.create_snapshot(ts, config_.settings().source_path, snapshots_dir, link_dest);
|
||||||
|
|
||||||
if (ok) {
|
if (ok) {
|
||||||
SnapshotInfo info;
|
SnapshotInfo info;
|
||||||
@@ -62,11 +55,10 @@ void SnapshotManager::delete_snapshot(const std::string& name) {
|
|||||||
std::string path = config_.snapshots_dir() + "/" + name;
|
std::string path = config_.snapshots_dir() + "/" + name;
|
||||||
std::string meta = config_.metadata_dir() + "/" + name + ".json";
|
std::string meta = config_.metadata_dir() + "/" + name + ".json";
|
||||||
|
|
||||||
/* Try sudo first (snapshots are owned by root), fallback to normal rm */
|
std::string cmd = "rm -rf \"" + path + "\"";
|
||||||
std::string cmd = "echo '' | sudo -S rm -rf \"" + path + "\" 2>/dev/null || rm -rf \"" + path + "\"";
|
|
||||||
system(cmd.c_str());
|
system(cmd.c_str());
|
||||||
|
|
||||||
std::string meta_cmd = "echo '' | sudo -S rm -f \"" + meta + "\" 2>/dev/null || rm -f \"" + meta + "\"";
|
std::string meta_cmd = "rm -f \"" + meta + "\"";
|
||||||
system(meta_cmd.c_str());
|
system(meta_cmd.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,14 +150,12 @@ std::vector<FileEntry> SnapshotManager::browse_directory(const std::string& snap
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool SnapshotManager::restore_selected(const std::string& snapshot_path,
|
bool SnapshotManager::restore_selected(const std::string& snapshot_path,
|
||||||
const std::vector<std::string>& files,
|
const std::vector<std::string>& files) {
|
||||||
const std::string& sudo_password) {
|
return rsync_.restore_files(snapshot_path, files, "/");
|
||||||
return rsync_.restore_files(snapshot_path, files, "/", sudo_password);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SnapshotManager::restore_full(const std::string& snapshot_path,
|
bool SnapshotManager::restore_full(const std::string& snapshot_path) {
|
||||||
const std::string& sudo_password) {
|
return rsync_.restore_full_snapshot(snapshot_path, "/");
|
||||||
return rsync_.restore_full_snapshot(snapshot_path, "/", sudo_password);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SnapshotManager::cleanup_old_snapshots() {
|
void SnapshotManager::cleanup_old_snapshots() {
|
||||||
|
|||||||
@@ -31,8 +31,7 @@ class SnapshotManager {
|
|||||||
public:
|
public:
|
||||||
SnapshotManager(Config& config, RsyncManager& rsync);
|
SnapshotManager(Config& config, RsyncManager& rsync);
|
||||||
|
|
||||||
std::string create_snapshot(const std::string& description = "",
|
std::string create_snapshot(const std::string& description = "");
|
||||||
const std::string& sudo_password = "");
|
|
||||||
void delete_snapshot(const std::string& name);
|
void delete_snapshot(const std::string& name);
|
||||||
std::vector<SnapshotInfo> list_snapshots();
|
std::vector<SnapshotInfo> list_snapshots();
|
||||||
SnapshotInfo get_snapshot_info(const std::string& name);
|
SnapshotInfo get_snapshot_info(const std::string& name);
|
||||||
@@ -41,11 +40,9 @@ public:
|
|||||||
const std::string& subpath = "");
|
const std::string& subpath = "");
|
||||||
|
|
||||||
bool restore_selected(const std::string& snapshot_path,
|
bool restore_selected(const std::string& snapshot_path,
|
||||||
const std::vector<std::string>& files,
|
const std::vector<std::string>& files);
|
||||||
const std::string& sudo_password);
|
|
||||||
|
|
||||||
bool restore_full(const std::string& snapshot_path,
|
bool restore_full(const std::string& snapshot_path);
|
||||||
const std::string& sudo_password);
|
|
||||||
|
|
||||||
void cleanup_old_snapshots();
|
void cleanup_old_snapshots();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user