From 64976e6afefff455ee9515218942a65c6ff5eede Mon Sep 17 00:00:00 2001 From: bescoto Date: Sat, 1 Feb 2003 08:13:05 +0000 Subject: Re-added --windows-mode option git-svn-id: http://svn.savannah.nongnu.org/svn/rdiff-backup/trunk@273 2b77aa54-bcbc-44c9-a7ec-4f6cf2b41109 --- rdiff-backup/CHANGELOG | 4 +- rdiff-backup/TODO | 2 + rdiff-backup/rdiff-backup.1 | 14 +----- rdiff-backup/rdiff_backup/FilenameMapping.py | 64 +++++++++++++++++++++++++--- rdiff-backup/rdiff_backup/Globals.py | 9 +--- rdiff-backup/rdiff_backup/Main.py | 38 ++++++++++------- rdiff-backup/rdiff_backup/Time.py | 14 +++--- rdiff-backup/rdiff_backup/increment.py | 11 ++++- rdiff-backup/rdiff_backup/restore.py | 36 ++++------------ rdiff-backup/rdiff_backup/rpath.py | 12 ++---- rdiff-backup/rdiff_backup/selection.py | 55 ++++-------------------- rdiff-backup/testing/FilenameMappingtest.py | 31 ++++++++++++++ rdiff-backup/testing/commontest.py | 2 +- rdiff-backup/testing/finaltest.py | 53 ++++++++++++++--------- 14 files changed, 188 insertions(+), 157 deletions(-) create mode 100644 rdiff-backup/testing/FilenameMappingtest.py diff --git a/rdiff-backup/CHANGELOG b/rdiff-backup/CHANGELOG index ba822a1..f9a4cd1 100644 --- a/rdiff-backup/CHANGELOG +++ b/rdiff-backup/CHANGELOG @@ -1,9 +1,11 @@ -New in v0.11.2 (2003/??/??) +New in v0.11.2 (2003/02/07) --------------------------- Fixed seg fault bug reported by a couple sparc/openbsd users. Thanks to Dave Steinberg for giving me an account on his system for testing. +Re-enabled --windows-mode and filename quoting. + New in v0.11.1 (2002/12/31) --------------------------- diff --git a/rdiff-backup/TODO b/rdiff-backup/TODO index f7d2aa8..17c689f 100644 --- a/rdiff-backup/TODO +++ b/rdiff-backup/TODO @@ -1,3 +1,5 @@ +Look at Kent Borg's suggestion for restore options and digests. + Write some better selection test cases to test new Iterate_fast func. Work on killtest code - avoid returning a failure when a file is diff --git a/rdiff-backup/rdiff-backup.1 b/rdiff-backup/rdiff-backup.1 index cb565b7..377351a 100644 --- a/rdiff-backup/rdiff-backup.1 +++ b/rdiff-backup/rdiff-backup.1 @@ -362,18 +362,8 @@ This option is short for "--chars to quote A-Z: --windows-time-format --no-hard-links --exclude-special-files" and is appropriate when backing a normal unix file system to one that doesn't allow colons in filenames, is not case sensitive, and cannot store special files or -hard links. --windows-mode should not be necessary when backing up -one windows file system to another, although --windows-time-format -would still be required. If this switch is used for backing up, it -must also be used when restoring, listing increments, etc. -.TP -.B --windows-time-format -If this option is present, use underscores instead of colons in -increment files, so 2001-07-15T04:09:38-07:00 becomes -2001-07-15T04_09_38-07_00. This option may be useful under various -Microsoft file systems, which prohibit colons in filenames. If this -switch is used for backing up, it must also be used when restoring, -listing increments, etc. +hard links. If this switch is used for backing up, it must also be +used when restoring, listing increments, etc. .SH EXAMPLES Simplest case---backup directory foo to directory bar, with increments diff --git a/rdiff-backup/rdiff_backup/FilenameMapping.py b/rdiff-backup/rdiff_backup/FilenameMapping.py index e305f68..fdcd017 100644 --- a/rdiff-backup/rdiff_backup/FilenameMapping.py +++ b/rdiff-backup/rdiff_backup/FilenameMapping.py @@ -20,14 +20,17 @@ """Coordinate corresponding files with different names For instance, some source filenames may contain characters not allowed -on the mirror end. Also, if a source filename is very long (say 240 -characters), the extra characters added to related increments may put -them over the usual 255 character limit. +on the mirror end. These files must be called something different on +the mirror end, so we escape the offending characters with semicolons. + +One problem/complication is that all this escaping may put files over +the 256 or whatever limit on the length of file names. (We just don't +handle that error.) """ import re -import Globals, log +import Globals, log, rpath max_filename_length = 255 @@ -43,6 +46,8 @@ unquoting_regexp = None quoting_char = None +class QuotingException(Exception): pass + def set_init_quote_vals(): """Set quoting value from Globals on all conns""" for conn in Globals.connections: @@ -90,7 +95,54 @@ def unquote(path): def unquote_single(match): """Unquote a single quoted character""" - assert len(match.group()) == 4 - return chr(int(match.group()[1:])) + if not len(match.group()) == 4: + raise QuotingException("Quoted group wrong size: " + match.group()) + try: return chr(int(match.group()[1:])) + except ValueError: + raise QuotingException("Quoted out of range: " + match.group()) + +class QuotedRPath(rpath.RPath): + """RPath where the filename is quoted version of index + We use QuotedRPaths so we don't need to remember to quote RPaths + derived from this one (via append or new_index). Note that only + the index is quoted, not the base. + + """ + def __init__(self, connection, base, index = (), data = None): + """Make new QuotedRPath""" + quoted_index = tuple(map(quote, index)) + rpath.RPath.__init__(self, connection, base, quoted_index, data) + self.index = index + + def listdir(self): + """Return list of unquoted filenames in current directory + + We want them unquoted so that the results can be sorted + correctly and append()ed to the currect QuotedRPath. + + """ + return map(unquote, self.conn.os.listdir(self.path)) + + def __str__(self): + return "QuotedPath: %s\nIndex: %s\nData: %s" % \ + (self.path, self.index, self.data) + + def isincfile(self): + """Return true if path indicates increment, sets various variables""" + result = rpath.RPath.isincfile(self) + if result: self.inc_basestr = unquote(self.inc_basestr) + return result + +def get_quotedrpath(rp, separate_basename = 0): + """Return quoted version of rpath rp""" + assert not rp.index # Why would we starting quoting "in the middle"? + if separate_basename: + dirname, basename = rp.dirsplit() + return QuotedRPath(rp.conn, dirname, (unquote(basename),), rp.data) + else: return QuotedRPath(rp.conn, rp.base, (), rp.data) + +def get_quoted_sep_base(filename): + """Get QuotedRPath from filename assuming last bit is quoted""" + return get_quotedrpath(rpath.RPath(Globals.local_connection, filename), 1) diff --git a/rdiff-backup/rdiff_backup/Globals.py b/rdiff-backup/rdiff_backup/Globals.py index bb4195c..a06e246 100644 --- a/rdiff-backup/rdiff_backup/Globals.py +++ b/rdiff-backup/rdiff_backup/Globals.py @@ -102,19 +102,12 @@ changed_settings = [] # The RPath of the rdiff-backup-data directory. rbdir = None -# This string is used when recognizing and creating time strings. -# If the time_separator is ":", then W3 datetime strings like -# 2001-12-07T04:22:01-07:00 are produced. It can be set to "_" to -# make filenames that don't contain colons, which aren't allowed -# under MS windows NT. -time_separator = ":" - # quoting_enabled is true if we should quote certain characters in # filenames on the source side (see FilenameMapping for more # info). chars_to_quote is a string whose characters should be # quoted, and quoting_char is the character to quote with. quoting_enabled = None -chars_to_quote = "" +chars_to_quote = "A-Z:" quoting_char = ';' # If true, emit output intended to be easily readable by a diff --git a/rdiff-backup/rdiff_backup/Main.py b/rdiff-backup/rdiff_backup/Main.py index 3921c73..bfcc906 100644 --- a/rdiff-backup/rdiff_backup/Main.py +++ b/rdiff-backup/rdiff_backup/Main.py @@ -1,4 +1,3 @@ - # Copyright 2002 Ben Escoto # # This file is part of rdiff-backup. @@ -142,16 +141,21 @@ def parse_cmdlineoptions(arglist): sys.exit(0) elif opt == "-v" or opt == "--verbosity": Log.setverbosity(arg) elif opt == "--windows-mode": - Globals.set('time_separator', "_") Globals.set('chars_to_quote', "A-Z:") Globals.set('quoting_enabled', 1) Globals.set('preserve_hardlinks', 0) - select_opts.append(("--exclude-special-files", None)) - assert 0, "Windows mode doesn't work in this version!" - elif opt == '--windows-time-format': - Globals.set('time_separator', "_") else: Log.FatalError("Unknown option %s" % opt) +def isincfilename(path): + """Return true if path is of a (possibly quoted) increment file""" + rp = rpath.RPath(Globals.local_connection, path) + if Globals.quoting_enabled: + if not FilenameMapping.quoting_char: + FilenameMapping.set_init_quote_vals() + rp = FilenameMapping.get_quotedrpath(rp, separate_basename = 1) + result = rp.isincfile() + return result + def set_action(): """Check arguments and try to set action""" global action @@ -160,8 +164,7 @@ def set_action(): if l == 0: commandline_error("No arguments given") elif l == 1: action = "restore" elif l == 2: - if rpath.RPath(Globals.local_connection, args[0]).isincfile(): - action = "restore" + if isincfilename(args[0]): action = "restore" else: action = "backup" else: commandline_error("Too many arguments given") @@ -230,6 +233,8 @@ def Main(arglist): def Backup(rpin, rpout): """Backup, possibly incrementally, src_path to dest_path.""" + if Globals.quoting_enabled: + rpout = FilenameMapping.get_quotedrpath(rpout) SetConnections.BackupInitConnections(rpin.conn, rpout.conn) backup_set_select(rpin) backup_init_dirs(rpin, rpout) @@ -259,9 +264,9 @@ def backup_init_dirs(rpin, rpout): elif not rpin.isdir(): Log.FatalError("Source %s is not a directory" % rpin.path) - datadir = rpout.append("rdiff-backup-data") + datadir = rpout.append_path("rdiff-backup-data") SetConnections.UpdateGlobal('rbdir', datadir) - incdir = rpath.RPath(rpout.conn, os.path.join(datadir.path, "increments")) + incdir = datadir.append_path("increments") prevtime = backup_get_mirrortime() if rpout.lstat(): @@ -351,7 +356,7 @@ def RestoreAsOf(rpin, target): target - RPath of place to put restored file """ - restore_check_paths(rpin, target, 1) + rpin, rpout = restore_check_paths(rpin, target, 1) try: time = Time.genstrtotime(restore_timestr) except Time.TimeException, exc: Log.FatalError(str(exc)) restore_common(rpin, target, time) @@ -384,7 +389,9 @@ def restore_check_paths(rpin, rpout, restoreasof = None): if not restoreasof: if not rpin.lstat(): Log.FatalError("Source file %s does not exist" % rpin.path) - elif not rpin.isincfile(): + if Globals.quoting_enabled: + rpin = FilenameMapping.get_quotedrpath(rpin, 1) + if not rpin.isincfile(): Log.FatalError("""File %s does not look like an increment file. Try restoring from an increment file (the filenames look like @@ -438,7 +445,8 @@ def restore_get_root(rpin): i = i-1 else: Log.FatalError("Unable to find rdiff-backup-data directory") - rootrp = parent_dir + if not Globals.quoting_enabled: rootrp = parent_dir + else: rootrp = FilenameMapping.get_quotedrpath(parent_dir) Log("Using mirror root directory %s" % rootrp.path, 6) datadir = rootrp.append_path("rdiff-backup-data") @@ -476,7 +484,7 @@ def CalculateAverage(rps): def RemoveOlderThan(rootrp): """Remove all increment files older than a certain time""" - datadir = rootrp.append("rdiff-backup-data") + datadir = rootrp.append_path("rdiff-backup-data") if not datadir.lstat() or not datadir.isdir(): Log.FatalError("Unable to open rdiff-backup-data dir %s" % (datadir.path,)) @@ -487,7 +495,7 @@ def RemoveOlderThan(rootrp): Log("Deleting increment(s) before %s" % timep, 4) times_in_secs = [inc.getinctime() for inc in - restore.get_inclist(datadir.append("increments"))] + restore.get_inclist(datadir.append_path("increments"))] times_in_secs = filter(lambda t: t < time, times_in_secs) if not times_in_secs: Log.FatalError("No increments older than %s found" % timep) diff --git a/rdiff-backup/rdiff_backup/Time.py b/rdiff-backup/rdiff_backup/Time.py index ea85ca4..bdcfb52 100644 --- a/rdiff-backup/rdiff_backup/Time.py +++ b/rdiff-backup/rdiff_backup/Time.py @@ -61,9 +61,8 @@ def setprevtime_local(timeinseconds, timestr): def timetostring(timeinseconds): """Return w3 datetime compliant listing of timeinseconds""" - return time.strftime("%Y-%m-%dT%H" + Globals.time_separator + - "%M" + Globals.time_separator + "%S", - time.localtime(timeinseconds)) + gettzd() + s = time.strftime("%Y-%m-%dT%H:%M:%S", time.localtime(timeinseconds)) + return s + gettzd() def stringtotime(timestring): """Return time in seconds from w3 timestring @@ -75,8 +74,7 @@ def stringtotime(timestring): try: date, daytime = timestring[:19].split("T") year, month, day = map(int, date.split("-")) - hour, minute, second = map(int, - daytime.split(Globals.time_separator)) + hour, minute, second = map(int, daytime.split(":")) assert 1900 < year < 2100, year assert 1 <= month <= 12 assert 1 <= day <= 31 @@ -154,15 +152,13 @@ def gettzd(): hours, minutes = map(abs, divmod(offset, 60)) assert 0 <= hours <= 23 assert 0 <= minutes <= 59 - return "%s%02d%s%02d" % (prefix, hours, - Globals.time_separator, minutes) + return "%s%02d:%02d" % (prefix, hours, minutes) def tzdtoseconds(tzd): """Given w3 compliant TZD, return how far ahead UTC is""" if tzd == "Z": return 0 assert len(tzd) == 6 # only accept forms like +08:00 for now - assert (tzd[0] == "-" or tzd[0] == "+") and \ - tzd[3] == Globals.time_separator + assert (tzd[0] == "-" or tzd[0] == "+") and tzd[3] == ":" return -60 * (60 * int(tzd[:3]) + int(tzd[4:])) def cmp(time1, time2): diff --git a/rdiff-backup/rdiff_backup/increment.py b/rdiff-backup/rdiff_backup/increment.py index a9d5413..76578fe 100644 --- a/rdiff-backup/rdiff_backup/increment.py +++ b/rdiff-backup/rdiff_backup/increment.py @@ -85,12 +85,19 @@ def makedir(mirrordir, incpref): return dirsign def get_inc(rp, time, typestr): - """Return increment like rp but with time and typestr suffixes""" + """Return increment like rp but with time and typestr suffixes + + To avoid any quoting, the returned rpath has empty index, and the + whole filename is in the base (which is not quoted). + + """ addtostr = lambda s: "%s.%s.%s" % (s, Time.timetostring(time), typestr) if rp.index: incrp = rp.__class__(rp.conn, rp.base, rp.index[:-1] + (addtostr(rp.index[-1]),)) - else: incrp = rp.__class__(rp.conn, addtostr(rp.base), rp.index) + else: + dirname, basename = rp.dirsplit() + incrp = rp.__class__(rp.conn, dirname, (addtostr(basename),)) return incrp def get_inc_ext(rp, typestr, inctime = None): diff --git a/rdiff-backup/rdiff_backup/restore.py b/rdiff-backup/rdiff_backup/restore.py index ff09eee..3ff4ce8 100644 --- a/rdiff-backup/rdiff_backup/restore.py +++ b/rdiff-backup/rdiff_backup/restore.py @@ -21,8 +21,8 @@ from __future__ import generators import tempfile, os -import Globals, Time, Rdiff, Hardlink, FilenameMapping, rorpiter, \ - selection, rpath, log, backup, static, robust, metadata +import Globals, Time, Rdiff, Hardlink, rorpiter, selection, rpath, \ + log, backup, static, robust, metadata # This should be set to selection.Select objects over the source and @@ -52,20 +52,13 @@ def Restore(mirror_rp, inc_rpath, target, restore_to_time): def get_inclist(inc_rpath): """Returns increments with given base""" dirname, basename = inc_rpath.dirsplit() - parent_dir = rpath.RPath(inc_rpath.conn, dirname, ()) + parent_dir = inc_rpath.__class__(inc_rpath.conn, dirname, ()) if not parent_dir.isdir(): return [] # inc directory not created yet index = inc_rpath.index - if index: - get_inc_ext = lambda filename: \ - rpath.RPath(inc_rpath.conn, inc_rpath.base, - inc_rpath.index[:-1] + (filename,)) - else: get_inc_ext = lambda filename: \ - rpath.RPath(inc_rpath.conn, os.path.join(dirname, filename)) - inc_list = [] for filename in parent_dir.listdir(): - inc = get_inc_ext(filename) + inc = parent_dir.append(filename) if inc.isincfile() and inc.getincbase_str() == basename: inc_list.append(inc) return inc_list @@ -404,26 +397,17 @@ class RestoreFile: for mirror_rp, inc_pair in collated: if not inc_pair: inc_rp = self.inc_rp.new_index(mirror_rp.index) - if Globals.quoting_enabled: inc_rp.quote_path() inc_list = [] else: inc_rp, inc_list = inc_pair if not mirror_rp: mirror_rp = self.mirror_rp.new_index(inc_rp.index) - if Globals.quoting_enabled: mirror_rp.quote_path() yield RestoreFile(mirror_rp, inc_rp, inc_list) def yield_mirrorrps(self, mirrorrp): """Yield mirrorrps underneath given mirrorrp""" - if mirrorrp and mirrorrp.isdir(): - if Globals.quoting_enabled: - for rp in selection.get_quoted_dir_children(mirrorrp): - if rp.index != ('rdiff-backup-data',): yield rp - else: - dirlist = mirrorrp.listdir() - dirlist.sort() - for filename in dirlist: - rp = mirrorrp.append(filename) - if rp.index != ('rdiff-backup-data',): yield rp + for filename in robust.listrp(mirrorrp): + rp = mirrorrp.append(filename) + if rp.index != ('rdiff-backup-data',): yield rp def yield_inc_complexes(self, inc_rpath): """Yield (sub_inc_rpath, inc_list) IndexedTuples from given inc_rpath @@ -433,23 +417,19 @@ class RestoreFile: """ if not inc_rpath.isdir(): return - inc_dict = {} # dictionary of basenames:inc_lists + inc_dict = {} # dictionary of basenames:IndexedTuples(index, inc_list) dirlist = robust.listrp(inc_rpath) - if Globals.quoting_enabled: - dirlist = [FilenameMapping.unquote(fn) for fn in dirlist] def affirm_dict_indexed(basename): """Make sure the rid dictionary has given basename as key""" if not inc_dict.has_key(basename): sub_inc_rp = inc_rpath.append(basename) - if Globals.quoting_enabled: sub_inc_rp.quote_path() inc_dict[basename] = rorpiter.IndexedTuple(sub_inc_rp.index, (sub_inc_rp, [])) def add_to_dict(filename): """Add filename to the inc tuple dictionary""" rp = inc_rpath.append(filename) - if Globals.quoting_enabled: rp.quote_path() if rp.isincfile() and rp.getinctype() != 'data': basename = rp.getincbase_str() affirm_dict_indexed(basename) diff --git a/rdiff-backup/rdiff_backup/rpath.py b/rdiff-backup/rdiff_backup/rpath.py index 49b9d2a..fb2c255 100644 --- a/rdiff-backup/rdiff_backup/rpath.py +++ b/rdiff-backup/rdiff_backup/rpath.py @@ -36,7 +36,7 @@ are dealing with are local or remote. """ import os, stat, re, sys, shutil, gzip, socket, time -import Globals, FilenameMapping, Time, static, log +import Globals, Time, static, log class SkipFileException(Exception): @@ -592,12 +592,6 @@ class RPath(RORPath): s = self.conn.reval("lambda path: os.lstat(path).st_rdev", self.path) return (s >> 8, s & 0xff) - def quote_path(self): - """Set path from quoted version of index""" - quoted_list = [FilenameMapping.quote(path) for path in self.index] - self.path = "/".join([self.base] + quoted_list) - self.setdata() - def chmod(self, permissions): """Wrapper around os.chmod""" self.conn.os.chmod(self.path, permissions) @@ -843,7 +837,9 @@ class RPath(RORPath): def getincbase_str(self): """Return the base filename string of an increment file""" - return self.getincbase().dirsplit()[1] + rp = self.getincbase() + if rp.index: return rp.index[-1] + else: return rp.dirsplit()[1] def makedev(self, type, major, minor): """Make a special file with specified type, and major/minor nums""" diff --git a/rdiff-backup/rdiff_backup/selection.py b/rdiff-backup/rdiff_backup/selection.py index d58735b..45d4c8c 100644 --- a/rdiff-backup/rdiff_backup/selection.py +++ b/rdiff-backup/rdiff_backup/selection.py @@ -80,19 +80,12 @@ class Select: # This re should not match normal filenames, but usually just globs glob_re = re.compile("(.*[*?[]|ignorecase\\:)", re.I | re.S) - def __init__(self, rootrp, quoted_filenames = None): - """Select initializer. rpath is the root directory - - When files have quoted characters in them, quoted_filenames - should be true. Then RPath's index will be the unquoted - version. - - """ + def __init__(self, rootrp): + """Select initializer. rpath is the root directory""" assert isinstance(rootrp, rpath.RPath) self.selection_functions = [] self.rpath = rootrp self.prefix = self.rpath.path - self.quoting_on = Globals.quoting_enabled and quoted_filenames def set_iter(self, iterate_parents = None, sel_func = None): """Initialize more variables, get ready to iterate @@ -103,9 +96,7 @@ class Select: """ if not sel_func: sel_func = self.Select self.rpath.setdata() # this may have changed since Select init - if self.quoting_on: - self.iter = self.Iterate(self.rpath, self.Iterate, sel_func) - else: self.iter = self.Iterate_fast(self.rpath, sel_func) + self.iter = self.Iterate_fast(self.rpath, sel_func) # only iterate parents if we are not starting from beginning self.next = self.iter.next @@ -113,12 +104,7 @@ class Select: return self def Iterate_fast(self, rpath, sel_func): - """Like Iterate, but don't recur, saving time - - Only handles standard case (quoting off, starting from - beginning). - - """ + """Like Iterate, but don't recur, saving time""" def error_handler(exc, filename): Log("Error initializing file %s/%s" % (rpath.path, filename), 2) return None @@ -194,17 +180,12 @@ class Select: Log("Error initializing file %s/%s" % (rpath.path, filename), 2) return None - if self.quoting_on: - for subdir in get_quoted_dir_children(rpath): - for rp in rec_func(subdir, rec_func, sel_func): + for filename in robust.listrp(rpath): + new_rp = robust.check_common_error( + error_handler, rpath.append, [filename]) + if new_rp: + for rp in rec_func(new_rp, rec_func, sel_func): yield rp - else: - for filename in robust.listrp(rpath): - new_rp = robust.check_common_error( - error_handler, rpath.append, [filename]) - if new_rp: - for rp in rec_func(new_rp, rec_func, sel_func): - yield rp def Select(self, rp): """Run through the selection functions and return dominant val 0/1/2""" @@ -617,22 +598,4 @@ probably isn't what you meant.""" % return res -def get_quoted_dir_children(rpath): - """For rpath directory, return list of quoted children in dir - - This used to be in FilenameMapping, but was moved because it - depends on the robust.listrp routine. - - """ - if not rpath.isdir(): return [] - dir_pairs = [(FilenameMapping.unquote(filename), filename) - for filename in robust.listrp(rpath)] - dir_pairs.sort() # sort by real index, not quoted part - child_list = [] - for unquoted, filename in dir_pairs: - childrp = rpath.append(unquoted) - childrp.quote_path() - child_list.append(childrp) - return child_list - diff --git a/rdiff-backup/testing/FilenameMappingtest.py b/rdiff-backup/testing/FilenameMappingtest.py new file mode 100644 index 0000000..66ab786 --- /dev/null +++ b/rdiff-backup/testing/FilenameMappingtest.py @@ -0,0 +1,31 @@ +import unittest +from commontest import * +from rdiff_backup import FilenameMapping + +class FilenameMappingTest(unittest.TestCase): + """Test the FilenameMapping class, for quoting filenames""" + def setUp(self): + """Just initialize quoting - assume windows mode""" + FilenameMapping.set_init_quote_vals() + + def testBasicQuote(self): + """Test basic quoting and unquoting""" + filenames = ["hello", "HeLLo", "EUOeu/EUOeu", ":", "::::EU", "/:/:"] + for filename in filenames: + quoted = FilenameMapping.quote(filename) + assert FilenameMapping.unquote(quoted) == filename, filename + + def testQuotedRPath(self): + """Test the QuotedRPath class""" + + def testQuotedSepBase(self): + """Test get_quoted_sep_base function""" + path = ("/usr/local/mirror_metadata" + ".1969-12-31;08421;05833;05820-07;05800.data.gz") + qrp = FilenameMapping.get_quoted_sep_base(path) + assert qrp.base == "/usr/local", qrp.base + assert len(qrp.index) == 1, qrp.index + assert (qrp.index[0] == + "mirror_metadata.1969-12-31T21:33:20-07:00.data.gz") + +if __name__ == "__main__": unittest.main() diff --git a/rdiff-backup/testing/commontest.py b/rdiff-backup/testing/commontest.py index dae825a..1e6fa82 100644 --- a/rdiff-backup/testing/commontest.py +++ b/rdiff-backup/testing/commontest.py @@ -5,7 +5,7 @@ from rdiff_backup.rpath import RPath from rdiff_backup import Globals, Hardlink, SetConnections, Main, \ selection, lazy, Time, rpath -SourceDir = "../src" +SourceDir = "../rdiff_backup" AbsCurdir = os.getcwd() # Absolute path name of current directory AbsTFdir = AbsCurdir+"/testfiles" MiscDir = "../misc" diff --git a/rdiff-backup/testing/finaltest.py b/rdiff-backup/testing/finaltest.py index ae48462..60bc072 100644 --- a/rdiff-backup/testing/finaltest.py +++ b/rdiff-backup/testing/finaltest.py @@ -1,6 +1,6 @@ import unittest, os, re, sys, time from commontest import * -from rdiff_backup import Globals, log, rpath, robust +from rdiff_backup import Globals, log, rpath, robust, FilenameMapping """Regression tests""" @@ -43,7 +43,7 @@ class PathSetter(unittest.TestCase): def reset_schema(self): self.rb_schema = SourceDir + \ - "/../rdiff-backup -v7 --remote-schema './chdir-wrapper2 %s' " + "/../rdiff-backup -v5 --remote-schema './chdir-wrapper2 %s' " def refresh(self, *rp_list): """Reread data for the given rps""" @@ -60,20 +60,21 @@ class PathSetter(unittest.TestCase): def exec_rb(self, time, *args): """Run rdiff-backup on given arguments""" arglist = [] - if time: arglist.append("--current-time %s" % str(time)) + if time: arglist.extend(["--current-time", str(time)]) arglist.append(self.src_prefix + args[0]) if len(args) > 1: arglist.append(self.dest_prefix + args[1]) assert len(args) == 2 - cmdstr = self.rb_schema + ' '.join(arglist) + argstring = ' '.join(map(lambda s: "'%s'" % (s,), arglist)) + cmdstr = self.rb_schema + argstring print "executing " + cmdstr assert not os.system(cmdstr) def exec_rb_extra_args(self, time, extra_args, *args): """Run rdiff-backup on given arguments""" arglist = [] - if time: arglist.append("--current-time %s" % str(time)) + if time: arglist.extend(["--current-time", str(time)]) arglist.append(self.src_prefix + args[0]) if len(args) > 1: arglist.append(self.dest_prefix + args[1]) @@ -166,14 +167,19 @@ class PathSetter(unittest.TestCase): "testfiles/output/rdiff-backup-data/increments/nochange")) assert nochange_incs == 1 or nochange_incs == 0, nochange_incs - def getinc_paths(self, basename, directory): + def getinc_paths(self, basename, directory, quoted = 0): """Return increment.______.dir paths""" - dirrp = rpath.RPath(Globals.local_connection, directory) - incfiles = [filename for filename in robust.listrp(dirrp) - if filename.startswith(basename)] - incfiles.sort() - incrps = map(lambda f: rpath.RPath(lc, directory+"/"+f), incfiles) - return map(lambda x: x.path, filter(rpath.RPath.isincfile, incrps)) + if quoted: + FilenameMapping.set_init_quote_vals() + dirrp = FilenameMapping.QuotedRPath(Globals.local_connection, + directory) + else: dirrp = rpath.RPath(Globals.local_connection, directory) + incbasenames = [filename for filename in robust.listrp(dirrp) + if filename.startswith(basename)] + incbasenames.sort() + incrps = map(dirrp.append, incbasenames) + return map(lambda x: x.path, + filter(lambda incrp: incrp.isincfile(), incrps)) class Final(PathSetter): @@ -204,7 +210,7 @@ class Final(PathSetter): self.exec_rb(None, '../../../../../../proc', 'testfiles/procoutput') def testProcRemote(self): - """Test mirroring proc""" + """Test mirroring proc remote""" Myrm("testfiles/procoutput") self.set_connections(None, None, "test2/tmp/", "../../") self.exec_rb(None, '../../../../../../proc', 'testfiles/procoutput') @@ -226,12 +232,13 @@ class Final(PathSetter): Globals.time_separator = "_" inc_paths = self.getinc_paths("increments.", - "testfiles/output/rdiff-backup-data") + "testfiles/output/rdiff-backup-data", 1) Globals.time_separator = ":" - assert len(inc_paths) == 1 + assert len(inc_paths) == 1, inc_paths # Restore increment2 self.exec_rb(None, inc_paths[0], 'testfiles/restoretarget2') - assert CompareRecursive(Local.inc2rp, Local.rpout2) + assert CompareRecursive(Local.inc2rp, Local.rpout2, + compare_hardlinks = 0) # Now check to make sure no ":" in output directory popen_fp = os.popen("find testfiles/output -name '*:*' | wc") @@ -242,6 +249,10 @@ class Final(PathSetter): class FinalSelection(PathSetter): """Test selection options""" + def run(cmd): + print "Executing: ", cmd + assert not os.system(cmd) + def testSelLocal(self): """Quick backup testing a few selection options""" self.delete_tmpdirs() @@ -277,11 +288,11 @@ testfiles/increment2/changed_dir""") # Test selective restoring mirror_rp = rpath.RPath(Globals.local_connection, "testfiles/output") restore_filename = get_increment_rp(mirror_rp, 10000).path - assert not os.system(self.rb_schema + - "--include testfiles/restoretarget1/various_file_types/" - "regular_file " - "--exclude '**' " + - restore_filename + " testfiles/restoretarget1") + self.run(self.rb_schema + + "--include testfiles/restoretarget1/various_file_types/" + "regular_file " + "--exclude '**' " + + restore_filename + " testfiles/restoretarget1") assert os.lstat("testfiles/restoretarget1/various_file_types/" "regular_file") self.assertRaises(OSError, os.lstat, "testfiles/restoretarget1/tester") -- cgit v1.2.1