From 3852649621c640e01ee4077f2dd77b506fe6be7f Mon Sep 17 00:00:00 2001 From: owsla Date: Sun, 12 Oct 2008 02:21:30 +0000 Subject: Automatically resume after a failed initial backup. (Patch from Josh Nisly) git-svn-id: http://svn.savannah.nongnu.org/svn/rdiff-backup/trunk@950 2b77aa54-bcbc-44c9-a7ec-4f6cf2b41109 --- rdiff-backup/rdiff_backup/Main.py | 40 +++++++++++++++++++++++++++++++++++ rdiff-backup/rdiff_backup/Security.py | 4 +++- rdiff-backup/rdiff_backup/rpath.py | 23 ++++++++++++++++++++ 3 files changed, 66 insertions(+), 1 deletion(-) (limited to 'rdiff-backup/rdiff_backup') diff --git a/rdiff-backup/rdiff_backup/Main.py b/rdiff-backup/rdiff_backup/Main.py index a286842..9ba0dce 100644 --- a/rdiff-backup/rdiff_backup/Main.py +++ b/rdiff-backup/rdiff_backup/Main.py @@ -380,6 +380,44 @@ def backup_check_dirs(rpin, rpout): Log.FatalError("Source %s is not a directory" % rpin.path) Globals.rbdir = rpout.append_path("rdiff-backup-data") +def check_failed_initial_backup(): + """Returns true if it looks like initial backup failed.""" + if Globals.rbdir.lstat(): + rbdir_files = Globals.rbdir.listdir() + mirror_markers = filter(lambda x: x.startswith("current_mirror"), + rbdir_files) + error_logs = filter(lambda x: x.startswith("error_log"), + rbdir_files) + metadata_mirrors = filter(lambda x: x.startswith("mirror_metadata"), + rbdir_files) + # If we have no current_mirror marker, and the increments directory + # is empty, we most likely have a failed backup. + return not mirror_markers and len(error_logs) <= 1 and \ + len(metadata_mirrors) <= 1 + return False + +def fix_failed_initial_backup(): + """Clear Globals.rbdir after a failed initial backup""" + Log("Found interrupted initial backup. Removing...", 2) + rbdir_files = Globals.rbdir.listdir() + # Try to delete the increments dir first + if 'increments' in rbdir_files: + rbdir_files.remove('increments') + rp = Globals.rbdir.append('increments') + try: + rp.conn.rpath.delete_dir_no_files(rp) + except rpath.RPathException: + Log("Increments dir contains files.", 4) + return + except Security.Violation: + Log("Server doesn't support resuming.", 2) + return + + for file_name in rbdir_files: + rp = Globals.rbdir.append_path(file_name) + if not rp.isdir(): # Only remove files, not folders + rp.delete() + def backup_set_rbdir(rpin, rpout): """Initialize data dir and logging""" global incdir @@ -400,6 +438,8 @@ exists, but does not look like a rdiff-backup directory. Running rdiff-backup like this could mess up what is currently in it. If you want to update or overwrite it, run rdiff-backup with the --force option.""" % rpout.path) + elif check_failed_initial_backup(): + fix_failed_initial_backup() if not Globals.rbdir.lstat(): Globals.rbdir.mkdir() SetConnections.UpdateGlobal('rbdir', Globals.rbdir) diff --git a/rdiff-backup/rdiff_backup/Security.py b/rdiff-backup/rdiff_backup/Security.py index 8dc26bc..a259221 100644 --- a/rdiff-backup/rdiff_backup/Security.py +++ b/rdiff-backup/rdiff_backup/Security.py @@ -45,7 +45,8 @@ file_requests = {'os.listdir':0, 'rpath.make_file_dict':0, 'os.chmod':0, 'os.chown':0, 'os.remove':0, 'os.removedirs':0, 'os.rename':0, 'os.renames':0, 'os.rmdir':0, 'os.unlink':0, 'os.utime':0, 'os.lchown':0, 'os.link':1, 'os.symlink':1, - 'os.mkdir':0, 'os.makedirs':0} + 'os.mkdir':0, 'os.makedirs':0, + 'rpath.delete_dir_no_files':0} def initialize(action, cmdpairs): """Initialize allowable request list and chroot""" @@ -180,6 +181,7 @@ def set_allowed_requests(sec_level): if sec_level == "all": l.extend(["os.mkdir", "os.chown", "os.lchown", "os.rename", "os.unlink", "os.remove", "os.chmod", "os.makedirs", + "rpath.delete_dir_no_files", "backup.DestinationStruct.patch", "restore.TargetStruct.get_initial_iter", "restore.TargetStruct.patch", diff --git a/rdiff-backup/rdiff_backup/rpath.py b/rdiff-backup/rdiff_backup/rpath.py index b3efc8c..a75cc0e 100644 --- a/rdiff-backup/rdiff_backup/rpath.py +++ b/rdiff-backup/rdiff_backup/rpath.py @@ -372,6 +372,14 @@ def get_incfile_info(basename): else: basestr = ".".join(dotsplit[:-2]) return (compressed, timestring, ext, basestr) +def delete_dir_no_files(rp): + """Deletes the directory at rp.path if empty. Raises if the + directory contains files.""" + assert rp.isdir() + if rp.contains_files(): + raise RPathException("Directory contains files.") + rp.delete() + class RORPath: """Read Only RPath - carry information about a path @@ -1047,6 +1055,21 @@ class RPath(RORPath): else: self.conn.os.unlink(self.path) self.setdata() + def contains_files(self): + """Returns true if self (or subdir) contains any regular files.""" + log.Log("Determining if directory contains files: %s" % self.path, 7) + if not self.isdir(): + return False + dir_entries = self.listdir() + for entry in dir_entries: + child_rp = self.append(entry) + if not child_rp.isdir(): + return True + else: + if child_rp.contains_files(): + return True + return False + def quote(self): """Return quoted self.path for use with os.system()""" return '"%s"' % self.regex_chars_to_quote.sub( -- cgit v1.2.1