summaryrefslogtreecommitdiff
path: root/rdiff-backup
diff options
context:
space:
mode:
Diffstat (limited to 'rdiff-backup')
-rw-r--r--rdiff-backup/rdiff-backup.19
-rw-r--r--rdiff-backup/rdiff_backup/Main.py80
-rw-r--r--rdiff-backup/rdiff_backup/Security.py2
-rw-r--r--rdiff-backup/rdiff_backup/restore.py21
4 files changed, 88 insertions, 24 deletions
diff --git a/rdiff-backup/rdiff-backup.1 b/rdiff-backup/rdiff-backup.1
index eea7d15..d749c17 100644
--- a/rdiff-backup/rdiff-backup.1
+++ b/rdiff-backup/rdiff-backup.1
@@ -75,6 +75,15 @@ option on the destination dir will undo the failed directory. This
happens automatically if you attempt to back up to a directory and the
last backup failed.
.TP
+.B --compare
+This is equivalent to
+.BI '--compare-at-time " now" '
+.TP
+.BI "--compare-at-time " time
+Compare a directory with the backup set at the given time. This can
+be useful to see how archived data differs from current data, or to
+check that a backup is current.
+.TP
.BI "--current-time " seconds
This option is useful mainly for testing. If set, rdiff-backup will
it for the current time instead of consulting the clock. The argument
diff --git a/rdiff-backup/rdiff_backup/Main.py b/rdiff-backup/rdiff_backup/Main.py
index e417013..1177296 100644
--- a/rdiff-backup/rdiff_backup/Main.py
+++ b/rdiff-backup/rdiff_backup/Main.py
@@ -49,18 +49,18 @@ def parse_cmdlineoptions(arglist):
try: optlist, args = getopt.getopt(arglist, "blr:sv:V",
["backup-mode", "calculate-average", "check-destination-dir",
- "current-time=", "exclude=", "exclude-device-files",
- "exclude-filelist=", "exclude-filelist-stdin",
- "exclude-globbing-filelist=", "exclude-mirror=",
- "exclude-other-filesystems", "exclude-regexp=",
- "exclude-special-files", "force", "group-mapping-file=",
- "include=", "include-filelist=", "include-filelist-stdin",
- "include-globbing-filelist=", "include-regexp=",
- "list-at-time=", "list-changed-since=", "list-increments",
- "list-increment-sizes", "never-drop-acls", "no-acls",
- "no-compare-inode", "no-compression",
- "no-compression-regexp=", "no-eas", "no-file-statistics",
- "no-hard-links", "null-separator",
+ "compare", "compare-at-time=", "current-time=", "exclude=",
+ "exclude-device-files", "exclude-filelist=",
+ "exclude-filelist-stdin", "exclude-globbing-filelist=",
+ "exclude-mirror=", "exclude-other-filesystems",
+ "exclude-regexp=", "exclude-special-files", "force",
+ "group-mapping-file=", "include=", "include-filelist=",
+ "include-filelist-stdin", "include-globbing-filelist=",
+ "include-regexp=", "list-at-time=", "list-changed-since=",
+ "list-increments", "list-increment-sizes",
+ "never-drop-acls", "no-acls", "no-compare-inode",
+ "no-compression", "no-compression-regexp=", "no-eas",
+ "no-file-statistics", "no-hard-links", "null-separator",
"override-chars-to-quote=", "parsable-output",
"print-statistics", "remote-cmd=", "remote-schema=",
"remove-older-than=", "restore-as-of=", "restrict=",
@@ -74,6 +74,10 @@ def parse_cmdlineoptions(arglist):
if opt == "-b" or opt == "--backup-mode": action = "backup"
elif opt == "--calculate-average": action = "calculate-average"
elif opt == "--check-destination-dir": action = "check-destination-dir"
+ elif opt == "--compare" or opt == "--compare-at-time":
+ action = "compare"
+ if opt == "--compare": restore_timestr = "now"
+ else: restore_timestr = arg
elif opt == "--current-time":
Globals.set_integer('current_time', arg)
elif opt == "--exclude": select_opts.append((opt, arg))
@@ -161,7 +165,7 @@ def check_action():
1: ['list-increments', 'list-increment-sizes',
'remove-older-than', 'list-at-time',
'list-changed-since', 'check-destination-dir'],
- 2: ['backup', 'restore', 'restore-as-of']}
+ 2: ['backup', 'restore', 'restore-as-of', 'compare']}
l = len(args)
if l == 0 and action not in arg_action_dict[l]:
commandline_error("No arguments given")
@@ -218,16 +222,17 @@ def take_action(rps):
connection.PipeConnection(sys.stdin, sys.stdout).Server()
sys.exit(0)
elif action == "backup": Backup(rps[0], rps[1])
- elif action == "restore": Restore(*rps)
- elif action == "restore-as-of": Restore(rps[0], rps[1], 1)
- elif action == "test-server": SetConnections.TestConnections()
+ elif action == "calculate-average": CalculateAverage(rps)
+ elif action == "check-destination-dir": CheckDest(rps[0])
+ elif action == "compare": Compare(*rps)
elif action == "list-at-time": ListAtTime(rps[0])
elif action == "list-changed-since": ListChangedSince(rps[0])
elif action == "list-increments": ListIncrements(rps[0])
elif action == 'list-increment-sizes': ListIncrementSizes(rps[0])
elif action == "remove-older-than": RemoveOlderThan(rps[0])
- elif action == "calculate-average": CalculateAverage(rps)
- elif action == "check-destination-dir": CheckDest(rps[0])
+ elif action == "restore": Restore(*rps)
+ elif action == "restore-as-of": Restore(rps[0], rps[1], 1)
+ elif action == "test-server": SetConnections.TestConnections()
else: raise AssertionError("Unknown action " + action)
def cleanup():
@@ -622,7 +627,7 @@ def restore_set_root(rpin):
def ListIncrements(rp):
"""Print out a summary of the increments and their times"""
- assert restore_set_root(rp)
+ require_root_set(rp)
restore_check_backup_dir(restore_root)
mirror_rp = restore_root.new_index(restore_index)
inc_rpath = Globals.rbdir.append_path('increments', restore_index)
@@ -632,10 +637,16 @@ def ListIncrements(rp):
print manage.describe_incs_parsable(incs, mirror_time, mirror_rp)
else: print manage.describe_incs_human(incs, mirror_time, mirror_rp)
+def require_root_set(rp):
+ """Like restore_set_root, but abort with error if directory not set"""
+ if not restore_set_root(rp):
+ Log.FatalError(("Bad directory %s.\n" % (rp.path,)) +
+ "It doesn't appear to be an rdiff-backup destination dir")
+
def ListIncrementSizes(rp):
"""Print out a summary of the increments """
- assert restore_set_root(rp)
+ require_root_set(rp)
restore_check_backup_dir(restore_root)
print manage.ListIncrementSizes(restore_root, restore_index)
@@ -688,20 +699,20 @@ def rot_check_dir(rootrp):
def ListChangedSince(rp):
"""List all the files under rp that have changed since restoretime"""
- assert restore_set_root(rp)
+ require_root_set(rp)
try: rest_time = Time.genstrtotime(restore_timestr)
except Time.TimeException, exc: Log.FatalError(str(exc))
restore_check_backup_dir(restore_root)
mirror_rp = restore_root.new_index(restore_index)
inc_rp = mirror_rp.append_path("increments", restore_index)
for rorp in rp.conn.restore.ListChangedSince(mirror_rp, inc_rp, rest_time):
- # This is a hack, see restore.ListChangedSince for rational
+ # This is a hack, see restore.ListChangedSince for rationale
print rorp.index[0]
def ListAtTime(rp):
"""List files in archive under rp that are present at restoretime"""
- assert restore_set_root(rp)
+ require_root_set(rp)
try: rest_time = Time.genstrtotime(restore_timestr)
except Time.TimeException, exc: Log.FatalError(str(exc))
restore_check_backup_dir(restore_root)
@@ -711,6 +722,29 @@ def ListAtTime(rp):
print rorp.get_indexpath()
+def Compare(src_rp, dest_rp, compare_time = None):
+ """Compare metadata in src_rp with metadata of backup session
+
+ Prints to stdout whenever a file in the src_rp directory has
+ different metadata than what is recorded in the metadata for the
+ appropriate session.
+
+ Session time is read from restore_timestr if compare_time is None.
+
+ """
+ require_root_set(dest_rp)
+ if not compare_time:
+ try: compare_time = Time.getstrtotime(restore_timestr)
+ except Time.TimeException, exc: Log.FatalError(str(exc))
+ restore_check_backup_dir(restore_root)
+
+ mirror_rp = restore_root.new_index(restore_index)
+ inc_rp = mirror_rp.append_path("increments", restore_index)
+ backup_set_select(src_rp) # Sets source rorp iterator
+ restore.Compare(src_rp.conn.backup.SourceStruct.get_source_select(),
+ src_iter, mirror_rp, inc_rp, compare_time)
+
+
def CheckDest(dest_rp):
"""Check the destination directory, """
if Globals.chars_to_quote:
diff --git a/rdiff-backup/rdiff_backup/Security.py b/rdiff-backup/rdiff_backup/Security.py
index 7ccb49a..b9000cc 100644
--- a/rdiff-backup/rdiff_backup/Security.py
+++ b/rdiff-backup/rdiff_backup/Security.py
@@ -96,7 +96,7 @@ def set_security_level(action, cmdpairs):
rdir = getpath(cp2)
elif action in ["test-server", "list-increments", 'list-increment-sizes',
"list-at-time", "list-changed-since",
- "calculate-average", "remove-older-than"]:
+ "calculate-average", "remove-older-than", "compare"]:
sec_level = "minimal"
rdir = tempfile.gettempdir()
else: assert 0, "Unknown action %s" % action
diff --git a/rdiff-backup/rdiff_backup/restore.py b/rdiff-backup/rdiff_backup/restore.py
index 86795b1..af097b1 100644
--- a/rdiff-backup/rdiff_backup/restore.py
+++ b/rdiff-backup/rdiff_backup/restore.py
@@ -98,6 +98,27 @@ def ListAtTime(mirror_rp, inc_rp, time):
old_iter = MirrorStruct.get_mirror_rorp_iter(_rest_time, 1)
for rorp in old_iter: yield rorp
+def Compare(src_iter, mirror_rp, inc_rp, compare_time):
+ """Compares metadata in src_rp dir with metadata in mirror_rp at time"""
+ MirrorStruct.set_mirror_and_rest_times(compare_time)
+ MirrorStruct.initialize_rf_cache(mirror_rp, inc_rp)
+
+ mir_iter = MirrorStruct.get_mirror_rorp_iter(compare_time, 1)
+ collated = rorpiter.Collate2Iters(src_iter, mir_iter)
+ changed_files_found = 0
+ for src_rorp, mir_rorp in collated:
+ if src_rorp == mir_rorp: continue
+ changed_files_found = 1
+ if not mir_rorp: change = "new"
+ elif not src_rorp: change = "deleted"
+ else: change = "changed"
+ path_desc = (src_rorp and src_rorp.get_indexpath() or
+ mir_rorp.get_indexpath())
+ Log("%-7s %s" % (change, path_desc), 3)
+ if not changed_file_found:
+ Log("No changes found. Directory matches archive data.", 3)
+ MirrorStruct.close_rf_cache()
+
class MirrorStruct:
"""Hold functions to be run on the mirror side"""