new method to detect backup
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -136,16 +136,20 @@ class EncryptionManager:
|
||||
def is_encrypted(self, base_dest_path: str) -> bool:
|
||||
return os.path.exists(self.get_container_path(base_dest_path))
|
||||
|
||||
def get_mount_point(self, base_dest_path: str) -> str:
|
||||
"""Constructs the unique, static mount point path for a given destination."""
|
||||
username = os.path.basename(base_dest_path.rstrip('/'))
|
||||
mapper_name = f"pybackup_luks_{username}"
|
||||
return os.path.join("/mnt", mapper_name)
|
||||
|
||||
def is_mounted(self, base_dest_path: str) -> bool:
|
||||
pybackup_dir = os.path.join(base_dest_path, "pybackup")
|
||||
mount_point = os.path.join(pybackup_dir, "encrypted")
|
||||
mount_point = self.get_mount_point(base_dest_path)
|
||||
return os.path.ismount(mount_point) or base_dest_path in self.mounted_destinations
|
||||
|
||||
def mount_for_deletion(self, base_dest_path: str, is_system: bool, password: str) -> Optional[str]:
|
||||
self.logger.log("Mounting container for deletion operation.")
|
||||
if self._open_and_mount(base_dest_path, is_system, password):
|
||||
mount_point = os.path.join(os.path.dirname(
|
||||
self.get_container_path(base_dest_path)), "..", "encrypted")
|
||||
mount_point = self.get_mount_point(base_dest_path)
|
||||
self.mounted_destinations.add(base_dest_path)
|
||||
return mount_point
|
||||
self.logger.log("Failed to mount container for deletion.")
|
||||
@@ -162,8 +166,7 @@ class EncryptionManager:
|
||||
self.logger.log("Handling existing container.")
|
||||
|
||||
username = os.path.basename(base_dest_path.rstrip('/'))
|
||||
mount_point = os.path.join(os.path.dirname(
|
||||
self.get_container_path(base_dest_path)), "..", "encrypted")
|
||||
mount_point = self.get_mount_point(base_dest_path)
|
||||
|
||||
if not self.is_mounted(base_dest_path):
|
||||
if not self._open_and_mount(base_dest_path, is_system):
|
||||
@@ -232,8 +235,7 @@ mount \"/dev/mapper/{mapper_name}\" \"{mount_point}\"
|
||||
return None
|
||||
|
||||
container_path = self.get_container_path(base_dest_path)
|
||||
mount_point = os.path.join(os.path.dirname(
|
||||
container_path), "..", "encrypted")
|
||||
mount_point = self.get_mount_point(base_dest_path)
|
||||
mapper_name = f"pybackup_luks_{username}"
|
||||
chown_cmd = self._get_chown_command(mount_point, is_system)
|
||||
|
||||
@@ -263,8 +265,7 @@ mount \"/dev/mapper/{mapper_name}\" \"{mount_point}\"
|
||||
return False
|
||||
|
||||
container_path = self.get_container_path(base_dest_path)
|
||||
mount_point = os.path.join(os.path.dirname(
|
||||
container_path), "..", "encrypted")
|
||||
mount_point = self.get_mount_point(base_dest_path)
|
||||
mapper_name = f"pybackup_luks_{username}"
|
||||
chown_cmd = self._get_chown_command(mount_point, is_system)
|
||||
|
||||
@@ -291,9 +292,7 @@ mount \"/dev/mapper/{mapper_name}\" \"{mount_point}\"
|
||||
return
|
||||
|
||||
self.logger.log(f"Unmounting and resetting owner for {base_dest_path}")
|
||||
container_path = self.get_container_path(base_dest_path)
|
||||
mount_point = os.path.join(os.path.dirname(
|
||||
container_path), "..", "encrypted")
|
||||
mount_point = self.get_mount_point(base_dest_path)
|
||||
|
||||
script = f"""
|
||||
chown root:root \"{mount_point}\" || true
|
||||
|
||||
@@ -361,6 +361,6 @@ class Msg:
|
||||
"keyfile_settings": _("Keyfile Settings"), # New
|
||||
"backup_defaults_title": _("Backup Defaults"), # New
|
||||
"automation_settings_title": _("Automation Settings"), # New
|
||||
"create_add_key_file": _("Create/Add Key File"), # New
|
||||
"key_file_not_created": _("Key file not created."), # New
|
||||
"create_add_key_file": _("Create/Add Key File"), # New
|
||||
"key_file_not_created": _("Key file not created."), # New
|
||||
}
|
||||
|
||||
@@ -678,6 +678,9 @@ class MainApplication(tk.Tk):
|
||||
self.task_progress.stop()
|
||||
elif message_type == 'cancel_button_state':
|
||||
self.start_pause_button.config(state=value)
|
||||
elif message_type == 'current_path':
|
||||
self.current_backup_path = value
|
||||
app_logger.log(f"Set current backup path to: {value}")
|
||||
elif message_type == 'deletion_complete':
|
||||
self.actions._set_ui_state(True)
|
||||
self.backup_content_frame.hide_deletion_status()
|
||||
|
||||
@@ -27,41 +27,46 @@ class Actions:
|
||||
self.app.inkrementell_var.set(True)
|
||||
|
||||
def _update_backup_type_controls(self):
|
||||
# Only applies to system backups in backup mode
|
||||
if self.app.mode != 'backup' or self.app.left_canvas_data.get('folder') != "Computer":
|
||||
self._set_backup_type("full") # Default for user backups
|
||||
self.app.full_backup_cb.config(state='disabled')
|
||||
self.app.incremental_cb.config(state='disabled')
|
||||
return
|
||||
else:
|
||||
# Re-enable if we switch back to system backup
|
||||
source_name = self.app.left_canvas_data.get('folder')
|
||||
is_system_backup = (source_name == "Computer")
|
||||
|
||||
# Re-enable controls for user backups, disable for system unless conditions are met
|
||||
if not is_system_backup:
|
||||
self.app.full_backup_cb.config(state='normal')
|
||||
self.app.incremental_cb.config(state='normal')
|
||||
else: # System backup
|
||||
self.app.full_backup_cb.config(state='normal')
|
||||
self.app.incremental_cb.config(state='normal')
|
||||
|
||||
# If controls are forced by advanced settings, do nothing
|
||||
if self.app.full_backup_cb.cget('state') == 'disabled' and self.app.incremental_cb.cget('state') == 'disabled':
|
||||
# Handle forced settings from advanced config, which have top priority
|
||||
if self.app.config_manager.get_setting("force_full_backup", False):
|
||||
self._set_backup_type("full")
|
||||
self.app.full_backup_cb.config(state='disabled')
|
||||
self.app.incremental_cb.config(state='disabled')
|
||||
return
|
||||
if self.app.config_manager.get_setting("force_incremental_backup", False):
|
||||
self._set_backup_type("incremental")
|
||||
self.app.full_backup_cb.config(state='disabled')
|
||||
self.app.incremental_cb.config(state='disabled')
|
||||
return
|
||||
|
||||
full_backup_exists = False
|
||||
if self.app.destination_path and os.path.isdir(self.app.destination_path):
|
||||
pybackup_dir = os.path.join(self.app.destination_path, "pybackup")
|
||||
if not os.path.isdir(pybackup_dir):
|
||||
self._set_backup_type("full")
|
||||
return
|
||||
# Default to Full if no destination is set
|
||||
if not self.app.destination_path or not os.path.isdir(self.app.destination_path):
|
||||
self._set_backup_type("full")
|
||||
return
|
||||
|
||||
is_encrypted_backup = self.app.encrypted_var.get()
|
||||
is_encrypted = self.app.encrypted_var.get()
|
||||
|
||||
# Use the new detection logic for both user and system backups
|
||||
# Note: For system backups, source_name is "Computer". We might need a more specific profile name.
|
||||
# For now, we adapt it to the check_for_full_backup function's expectation.
|
||||
profile_name = "system" if is_system_backup else source_name
|
||||
|
||||
system_backups = self.app.backup_manager.list_system_backups(
|
||||
self.app.destination_path, mount_if_needed=False)
|
||||
|
||||
if system_backups is None: # Encrypted, but not inspected
|
||||
full_backup_exists = True # Assume one exists to be safe
|
||||
else:
|
||||
for backup in system_backups:
|
||||
# Match the encryption status and check if it's a full backup
|
||||
if backup.get('is_encrypted') == is_encrypted_backup and backup.get('backup_type_base') == 'Full':
|
||||
full_backup_exists = True
|
||||
break
|
||||
full_backup_exists = self.app.backup_manager.check_for_full_backup(
|
||||
dest_path=self.app.destination_path,
|
||||
source_name=profile_name, # Using a profile name now
|
||||
is_encrypted=is_encrypted
|
||||
)
|
||||
|
||||
if full_backup_exists:
|
||||
self._set_backup_type("incremental")
|
||||
@@ -643,32 +648,7 @@ class Actions:
|
||||
return
|
||||
|
||||
is_encrypted = self.app.encrypted_var.get()
|
||||
password = None
|
||||
if is_encrypted:
|
||||
username = os.path.basename(base_dest.rstrip('/'))
|
||||
password = self.app.backup_manager.encryption_manager.get_password(
|
||||
username, confirm=True)
|
||||
if not password:
|
||||
app_logger.log(
|
||||
"Encryption enabled, but no password provided. Aborting backup.")
|
||||
self.app.backup_is_running = False
|
||||
self.app.start_pause_button["text"] = Msg.STR["start"]
|
||||
self._set_ui_state(True)
|
||||
return
|
||||
|
||||
try:
|
||||
locale.setlocale(locale.LC_TIME, 'de_DE.UTF-8')
|
||||
except locale.Error:
|
||||
app_logger.log(
|
||||
"Could not set locale to de_DE.UTF-8. Using default.")
|
||||
|
||||
now = datetime.datetime.now()
|
||||
date_str = now.strftime("%d-%m-%Y")
|
||||
time_str = now.strftime("%H:%M:%S")
|
||||
folder_name = f"{date_str}_{time_str}_system_{mode}"
|
||||
# The backup_manager will add /pybackup/
|
||||
final_dest = os.path.join(base_dest, folder_name)
|
||||
self.app.current_backup_path = final_dest
|
||||
# Password handling is now managed within the backup manager if needed for mounting
|
||||
|
||||
source_size_bytes = self.app.left_canvas_data.get('total_bytes', 0)
|
||||
|
||||
@@ -686,8 +666,9 @@ class Actions:
|
||||
self.app.backup_manager.start_backup(
|
||||
queue=self.app.queue,
|
||||
source_path="/",
|
||||
dest_path=final_dest,
|
||||
dest_path=base_dest, # Pass the base destination path
|
||||
is_system=True,
|
||||
source_name="system",
|
||||
is_dry_run=is_dry_run,
|
||||
exclude_files=exclude_file_paths,
|
||||
source_size=source_size_bytes,
|
||||
@@ -710,30 +691,10 @@ class Actions:
|
||||
return
|
||||
|
||||
is_encrypted = self.app.encrypted_var.get()
|
||||
password = None
|
||||
if is_encrypted:
|
||||
username = os.path.basename(base_dest.rstrip('/'))
|
||||
password = self.app.backup_manager.encryption_manager.get_password(
|
||||
username, confirm=True)
|
||||
if not password:
|
||||
app_logger.log(
|
||||
"Encryption enabled, but no password provided. Aborting backup.")
|
||||
self.app.backup_is_running = False
|
||||
self.app.start_pause_button["text"] = Msg.STR["start"]
|
||||
self._set_ui_state(True)
|
||||
return
|
||||
# Password handling is now managed within the backup manager if needed for mounting
|
||||
|
||||
# Determine mode for user backup based on UI selection
|
||||
mode = "full" if self.app.vollbackup_var.get() else "incremental"
|
||||
|
||||
now = datetime.datetime.now()
|
||||
date_str = now.strftime("%d-%m-%Y")
|
||||
time_str = now.strftime("%H:%M:%S")
|
||||
folder_name = f"{date_str}_{time_str}_user_{source_name}_{mode}"
|
||||
|
||||
final_dest = os.path.join(base_dest, folder_name)
|
||||
self.app.current_backup_path = final_dest
|
||||
|
||||
is_dry_run = self.app.testlauf_var.get()
|
||||
is_compressed = self.app.compressed_var.get()
|
||||
use_trash_bin = self.app.config_manager.get_setting(
|
||||
@@ -744,10 +705,11 @@ class Actions:
|
||||
self.app.backup_manager.start_backup(
|
||||
queue=self.app.queue,
|
||||
source_path=source_path,
|
||||
dest_path=final_dest,
|
||||
dest_path=base_dest, # Pass the base destination path
|
||||
is_system=False,
|
||||
source_name=source_name,
|
||||
is_dry_run=is_dry_run,
|
||||
exclude_files=None,
|
||||
exclude_files=None, # User backups don't use the global exclude list here
|
||||
source_size=source_size_bytes,
|
||||
is_compressed=is_compressed,
|
||||
is_encrypted=is_encrypted,
|
||||
|
||||
@@ -94,15 +94,15 @@ class UserBackupContentFrame(ttk.Frame):
|
||||
if not selected_backup:
|
||||
return
|
||||
|
||||
is_encrypted = selected_backup.get('is_encrypted', False)
|
||||
info_file_name = f"{selected_item_id}{'_encrypted' if is_encrypted else ''}.txt"
|
||||
info_file_path = os.path.join(
|
||||
self.backup_path, "pybackup", info_file_name)
|
||||
|
||||
if not os.path.exists(info_file_path):
|
||||
self.backup_manager.update_comment(info_file_path, "")
|
||||
# Use the direct path to the info file, which we added to the backup dict
|
||||
info_file_path = selected_backup.get('info_file_path')
|
||||
if not info_file_path or not os.path.isfile(info_file_path):
|
||||
MessageDialog(self, message_type="error", title="Error", text=f"Metadata file not found: {info_file_path}")
|
||||
return
|
||||
|
||||
CommentEditorDialog(self, info_file_path, self.backup_manager)
|
||||
|
||||
# Refresh the view to show the new comment
|
||||
self.parent_view.show(self.backup_path)
|
||||
|
||||
def _restore_selected(self):
|
||||
@@ -129,28 +129,24 @@ class UserBackupContentFrame(ttk.Frame):
|
||||
password = None
|
||||
|
||||
if is_encrypted:
|
||||
username = os.path.basename(self.backup_path.rstrip('/'))
|
||||
# Get password in the UI thread before starting the background task
|
||||
password = self.backup_manager.encryption_manager.get_password(
|
||||
username, confirm=False)
|
||||
# For encrypted backups, the base_dest_path is the path to the container's parent directory
|
||||
# We assume the logic to get the username/keyring entry is handled by the encryption manager
|
||||
password = self.backup_manager.encryption_manager.get_password(confirm=False)
|
||||
if not password:
|
||||
self.actions.logger.log(
|
||||
"Password entry cancelled, aborting deletion.")
|
||||
return
|
||||
|
||||
info_file_to_delete = os.path.join(
|
||||
self.backup_path, "pybackup", f"{selected_item_id}{'_encrypted' if is_encrypted else ''}.txt")
|
||||
|
||||
self.actions._set_ui_state(False)
|
||||
self.parent_view.show_deletion_status(
|
||||
Msg.STR["deleting_backup_in_progress"])
|
||||
|
||||
# The info_file_path is no longer needed as it's inside the folder_to_delete
|
||||
self.backup_manager.start_delete_backup(
|
||||
path_to_delete=folder_to_delete,
|
||||
info_file_path=info_file_to_delete,
|
||||
is_encrypted=is_encrypted,
|
||||
is_system=False,
|
||||
base_dest_path=self.backup_path,
|
||||
base_dest_path=self.backup_path, # This is the root destination folder
|
||||
password=password,
|
||||
queue=self.winfo_toplevel().queue
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user