Commit 8274e191 authored by bescoto's avatar bescoto

Re-added --windows-mode option


git-svn-id: http://svn.savannah.nongnu.org/svn/rdiff-backup@273 2b77aa54-bcbc-44c9-a7ec-4f6cf2b41109
parent f38aa30d
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 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. 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) New in v0.11.1 (2002/12/31)
--------------------------- ---------------------------
......
Look at Kent Borg's suggestion for restore options and digests.
Write some better selection test cases to test new Iterate_fast func. Write some better selection test cases to test new Iterate_fast func.
Work on killtest code - avoid returning a failure when a file is Work on killtest code - avoid returning a failure when a file is
......
...@@ -362,18 +362,8 @@ This option is short for "--chars to quote A-Z: --windows-time-format ...@@ -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 --no-hard-links --exclude-special-files" and is appropriate when
backing a normal unix file system to one that doesn't allow colons in 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 filenames, is not case sensitive, and cannot store special files or
hard links. --windows-mode should not be necessary when backing up hard links. If this switch is used for backing up, it must also be
one windows file system to another, although --windows-time-format used when restoring, listing increments, etc.
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.
.SH EXAMPLES .SH EXAMPLES
Simplest case---backup directory foo to directory bar, with increments Simplest case---backup directory foo to directory bar, with increments
......
...@@ -20,14 +20,17 @@ ...@@ -20,14 +20,17 @@
"""Coordinate corresponding files with different names """Coordinate corresponding files with different names
For instance, some source filenames may contain characters not allowed For instance, some source filenames may contain characters not allowed
on the mirror end. Also, if a source filename is very long (say 240 on the mirror end. These files must be called something different on
characters), the extra characters added to related increments may put the mirror end, so we escape the offending characters with semicolons.
them over the usual 255 character limit.
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 re
import Globals, log import Globals, log, rpath
max_filename_length = 255 max_filename_length = 255
...@@ -43,6 +46,8 @@ unquoting_regexp = None ...@@ -43,6 +46,8 @@ unquoting_regexp = None
quoting_char = None quoting_char = None
class QuotingException(Exception): pass
def set_init_quote_vals(): def set_init_quote_vals():
"""Set quoting value from Globals on all conns""" """Set quoting value from Globals on all conns"""
for conn in Globals.connections: for conn in Globals.connections:
...@@ -90,7 +95,54 @@ def unquote(path): ...@@ -90,7 +95,54 @@ def unquote(path):
def unquote_single(match): def unquote_single(match):
"""Unquote a single quoted character""" """Unquote a single quoted character"""
assert len(match.group()) == 4 if not len(match.group()) == 4:
return chr(int(match.group()[1:])) 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)
...@@ -102,19 +102,12 @@ changed_settings = [] ...@@ -102,19 +102,12 @@ changed_settings = []
# The RPath of the rdiff-backup-data directory. # The RPath of the rdiff-backup-data directory.
rbdir = None 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 # quoting_enabled is true if we should quote certain characters in
# filenames on the source side (see FilenameMapping for more # filenames on the source side (see FilenameMapping for more
# info). chars_to_quote is a string whose characters should be # info). chars_to_quote is a string whose characters should be
# quoted, and quoting_char is the character to quote with. # quoted, and quoting_char is the character to quote with.
quoting_enabled = None quoting_enabled = None
chars_to_quote = "" chars_to_quote = "A-Z:"
quoting_char = ';' quoting_char = ';'
# If true, emit output intended to be easily readable by a # If true, emit output intended to be easily readable by a
......
# Copyright 2002 Ben Escoto # Copyright 2002 Ben Escoto
# #
# This file is part of rdiff-backup. # This file is part of rdiff-backup.
...@@ -142,16 +141,21 @@ def parse_cmdlineoptions(arglist): ...@@ -142,16 +141,21 @@ def parse_cmdlineoptions(arglist):
sys.exit(0) sys.exit(0)
elif opt == "-v" or opt == "--verbosity": Log.setverbosity(arg) elif opt == "-v" or opt == "--verbosity": Log.setverbosity(arg)
elif opt == "--windows-mode": elif opt == "--windows-mode":
Globals.set('time_separator', "_")
Globals.set('chars_to_quote', "A-Z:") Globals.set('chars_to_quote', "A-Z:")
Globals.set('quoting_enabled', 1) Globals.set('quoting_enabled', 1)
Globals.set('preserve_hardlinks', 0) 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) 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(): def set_action():
"""Check arguments and try to set action""" """Check arguments and try to set action"""
global action global action
...@@ -160,8 +164,7 @@ def set_action(): ...@@ -160,8 +164,7 @@ def set_action():
if l == 0: commandline_error("No arguments given") if l == 0: commandline_error("No arguments given")
elif l == 1: action = "restore" elif l == 1: action = "restore"
elif l == 2: elif l == 2:
if rpath.RPath(Globals.local_connection, args[0]).isincfile(): if isincfilename(args[0]): action = "restore"
action = "restore"
else: action = "backup" else: action = "backup"
else: commandline_error("Too many arguments given") else: commandline_error("Too many arguments given")
...@@ -230,6 +233,8 @@ def Main(arglist): ...@@ -230,6 +233,8 @@ def Main(arglist):
def Backup(rpin, rpout): def Backup(rpin, rpout):
"""Backup, possibly incrementally, src_path to dest_path.""" """Backup, possibly incrementally, src_path to dest_path."""
if Globals.quoting_enabled:
rpout = FilenameMapping.get_quotedrpath(rpout)
SetConnections.BackupInitConnections(rpin.conn, rpout.conn) SetConnections.BackupInitConnections(rpin.conn, rpout.conn)
backup_set_select(rpin) backup_set_select(rpin)
backup_init_dirs(rpin, rpout) backup_init_dirs(rpin, rpout)
...@@ -259,9 +264,9 @@ def backup_init_dirs(rpin, rpout): ...@@ -259,9 +264,9 @@ def backup_init_dirs(rpin, rpout):
elif not rpin.isdir(): elif not rpin.isdir():
Log.FatalError("Source %s is not a directory" % rpin.path) 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) SetConnections.UpdateGlobal('rbdir', datadir)
incdir = rpath.RPath(rpout.conn, os.path.join(datadir.path, "increments")) incdir = datadir.append_path("increments")
prevtime = backup_get_mirrortime() prevtime = backup_get_mirrortime()
if rpout.lstat(): if rpout.lstat():
...@@ -351,7 +356,7 @@ def RestoreAsOf(rpin, target): ...@@ -351,7 +356,7 @@ def RestoreAsOf(rpin, target):
target - RPath of place to put restored file 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) try: time = Time.genstrtotime(restore_timestr)
except Time.TimeException, exc: Log.FatalError(str(exc)) except Time.TimeException, exc: Log.FatalError(str(exc))
restore_common(rpin, target, time) restore_common(rpin, target, time)
...@@ -384,7 +389,9 @@ def restore_check_paths(rpin, rpout, restoreasof = None): ...@@ -384,7 +389,9 @@ def restore_check_paths(rpin, rpout, restoreasof = None):
if not restoreasof: if not restoreasof:
if not rpin.lstat(): if not rpin.lstat():
Log.FatalError("Source file %s does not exist" % rpin.path) 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. Log.FatalError("""File %s does not look like an increment file.
Try restoring from an increment file (the filenames look like Try restoring from an increment file (the filenames look like
...@@ -438,7 +445,8 @@ def restore_get_root(rpin): ...@@ -438,7 +445,8 @@ def restore_get_root(rpin):
i = i-1 i = i-1
else: Log.FatalError("Unable to find rdiff-backup-data directory") 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) Log("Using mirror root directory %s" % rootrp.path, 6)
datadir = rootrp.append_path("rdiff-backup-data") datadir = rootrp.append_path("rdiff-backup-data")
...@@ -476,7 +484,7 @@ def CalculateAverage(rps): ...@@ -476,7 +484,7 @@ def CalculateAverage(rps):
def RemoveOlderThan(rootrp): def RemoveOlderThan(rootrp):
"""Remove all increment files older than a certain time""" """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(): if not datadir.lstat() or not datadir.isdir():
Log.FatalError("Unable to open rdiff-backup-data dir %s" % Log.FatalError("Unable to open rdiff-backup-data dir %s" %
(datadir.path,)) (datadir.path,))
...@@ -487,7 +495,7 @@ def RemoveOlderThan(rootrp): ...@@ -487,7 +495,7 @@ def RemoveOlderThan(rootrp):
Log("Deleting increment(s) before %s" % timep, 4) Log("Deleting increment(s) before %s" % timep, 4)
times_in_secs = [inc.getinctime() for inc in 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) times_in_secs = filter(lambda t: t < time, times_in_secs)
if not times_in_secs: if not times_in_secs:
Log.FatalError("No increments older than %s found" % timep) Log.FatalError("No increments older than %s found" % timep)
......
...@@ -61,9 +61,8 @@ def setprevtime_local(timeinseconds, timestr): ...@@ -61,9 +61,8 @@ def setprevtime_local(timeinseconds, timestr):
def timetostring(timeinseconds): def timetostring(timeinseconds):
"""Return w3 datetime compliant listing of timeinseconds""" """Return w3 datetime compliant listing of timeinseconds"""
return time.strftime("%Y-%m-%dT%H" + Globals.time_separator + s = time.strftime("%Y-%m-%dT%H:%M:%S", time.localtime(timeinseconds))
"%M" + Globals.time_separator + "%S", return s + gettzd()
time.localtime(timeinseconds)) + gettzd()
def stringtotime(timestring): def stringtotime(timestring):
"""Return time in seconds from w3 timestring """Return time in seconds from w3 timestring
...@@ -75,8 +74,7 @@ def stringtotime(timestring): ...@@ -75,8 +74,7 @@ def stringtotime(timestring):
try: try:
date, daytime = timestring[:19].split("T") date, daytime = timestring[:19].split("T")
year, month, day = map(int, date.split("-")) year, month, day = map(int, date.split("-"))
hour, minute, second = map(int, hour, minute, second = map(int, daytime.split(":"))
daytime.split(Globals.time_separator))
assert 1900 < year < 2100, year assert 1900 < year < 2100, year
assert 1 <= month <= 12 assert 1 <= month <= 12
assert 1 <= day <= 31 assert 1 <= day <= 31
...@@ -154,15 +152,13 @@ def gettzd(): ...@@ -154,15 +152,13 @@ def gettzd():
hours, minutes = map(abs, divmod(offset, 60)) hours, minutes = map(abs, divmod(offset, 60))
assert 0 <= hours <= 23 assert 0 <= hours <= 23
assert 0 <= minutes <= 59 assert 0 <= minutes <= 59
return "%s%02d%s%02d" % (prefix, hours, return "%s%02d:%02d" % (prefix, hours, minutes)
Globals.time_separator, minutes)
def tzdtoseconds(tzd): def tzdtoseconds(tzd):
"""Given w3 compliant TZD, return how far ahead UTC is""" """Given w3 compliant TZD, return how far ahead UTC is"""
if tzd == "Z": return 0 if tzd == "Z": return 0
assert len(tzd) == 6 # only accept forms like +08:00 for now assert len(tzd) == 6 # only accept forms like +08:00 for now
assert (tzd[0] == "-" or tzd[0] == "+") and \ assert (tzd[0] == "-" or tzd[0] == "+") and tzd[3] == ":"
tzd[3] == Globals.time_separator
return -60 * (60 * int(tzd[:3]) + int(tzd[4:])) return -60 * (60 * int(tzd[:3]) + int(tzd[4:]))
def cmp(time1, time2): def cmp(time1, time2):
......
...@@ -85,12 +85,19 @@ def makedir(mirrordir, incpref): ...@@ -85,12 +85,19 @@ def makedir(mirrordir, incpref):
return dirsign return dirsign
def get_inc(rp, time, typestr): 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) addtostr = lambda s: "%s.%s.%s" % (s, Time.timetostring(time), typestr)
if rp.index: if rp.index:
incrp = rp.__class__(rp.conn, rp.base, rp.index[:-1] + incrp = rp.__class__(rp.conn, rp.base, rp.index[:-1] +
(addtostr(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 return incrp
def get_inc_ext(rp, typestr, inctime = None): def get_inc_ext(rp, typestr, inctime = None):
......
...@@ -21,8 +21,8 @@ ...@@ -21,8 +21,8 @@
from __future__ import generators from __future__ import generators
import tempfile, os import tempfile, os
import Globals, Time, Rdiff, Hardlink, FilenameMapping, rorpiter, \ import Globals, Time, Rdiff, Hardlink, rorpiter, selection, rpath, \
selection, rpath, log, backup, static, robust, metadata log, backup, static, robust, metadata
# This should be set to selection.Select objects over the source and # 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): ...@@ -52,20 +52,13 @@ def Restore(mirror_rp, inc_rpath, target, restore_to_time):
def get_inclist(inc_rpath): def get_inclist(inc_rpath):
"""Returns increments with given base""" """Returns increments with given base"""
dirname, basename = inc_rpath.dirsplit() 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 if not parent_dir.isdir(): return [] # inc directory not created yet
index = inc_rpath.index 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 = [] inc_list = []
for filename in parent_dir.listdir(): for filename in parent_dir.listdir():
inc = get_inc_ext(filename) inc = parent_dir.append(filename)
if inc.isincfile() and inc.getincbase_str() == basename: if inc.isincfile() and inc.getincbase_str() == basename:
inc_list.append(inc) inc_list.append(inc)
return inc_list return inc_list
...@@ -404,26 +397,17 @@ class RestoreFile: ...@@ -404,26 +397,17 @@ class RestoreFile:
for mirror_rp, inc_pair in collated: for mirror_rp, inc_pair in collated:
if not inc_pair: if not inc_pair:
inc_rp = self.inc_rp.new_index(mirror_rp.index) inc_rp = self.inc_rp.new_index(mirror_rp.index)
if Globals.quoting_enabled: inc_rp.quote_path()
inc_list = [] inc_list = []
else: inc_rp, inc_list = inc_pair else: inc_rp, inc_list = inc_pair
if not mirror_rp: if not mirror_rp:
mirror_rp = self.mirror_rp.new_index(inc_rp.index) 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) yield RestoreFile(mirror_rp, inc_rp, inc_list)
def yield_mirrorrps(self, mirrorrp): def yield_mirrorrps(self, mirrorrp):
"""Yield mirrorrps underneath given mirrorrp""" """Yield mirrorrps underneath given mirrorrp"""
if mirrorrp and mirrorrp.isdir(): for filename in robust.listrp(mirrorrp):
if Globals.quoting_enabled: rp = mirrorrp.append(filename)
for rp in selection.get_quoted_dir_children(mirrorrp): if rp.index != ('rdiff-backup-data',): yield rp
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
def yield_inc_complexes(self, inc_rpath): def yield_inc_complexes(self, inc_rpath):
"""Yield (sub_inc_rpath, inc_list) IndexedTuples from given inc_rpath """Yield (sub_inc_rpath, inc_list) IndexedTuples from given inc_rpath
...@@ -433,23 +417,19 @@ class RestoreFile: ...@@ -433,23 +417,19 @@ class RestoreFile:
""" """
if not inc_rpath.isdir(): return 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) dirlist = robust.listrp(inc_rpath)
if Globals.quoting_enabled:
dirlist = [FilenameMapping.unquote(fn) for fn in dirlist]
def affirm_dict_indexed(basename): def affirm_dict_indexed(basename):
"""Make sure the rid dictionary has given basename as key""" """Make sure the rid dictionary has given basename as key"""
if not inc_dict.has_key(basename): if not inc_dict.has_key(basename):
sub_inc_rp = inc_rpath.append(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, inc_dict[basename] = rorpiter.IndexedTuple(sub_inc_rp.index,
(sub_inc_rp, [])) (sub_inc_rp, []))
def add_to_dict(filename): def add_to_dict(filename):
"""Add filename to the inc tuple dictionary""" """Add filename to the inc tuple dictionary"""
rp = inc_rpath.append(filename) rp = inc_rpath.append(filename)
if Globals.quoting_enabled: rp.quote_path()
if rp.isincfile() and rp.getinctype() != 'data': if rp.isincfile() and rp.getinctype() != 'data':
basename = rp.getincbase_str() basename = rp.getincbase_str()
affirm_dict_indexed(basename) affirm_dict_indexed(basename)
......
...@@ -36,7 +36,7 @@ are dealing with are local or remote. ...@@ -36,7 +36,7 @@ are dealing with are local or remote.
""" """
import os, stat, re, sys, shutil, gzip, socket, time import os, stat, re, sys, shutil, gzip, socket, time
import Globals, FilenameMapping, Time, static, log import Globals, Time, static, log
class SkipFileException(Exception): class SkipFileException(Exception):
...@@ -592,12 +592,6 @@ class RPath(RORPath): ...@@ -592,12 +592,6 @@ class RPath(RORPath):
s = self.conn.reval("lambda path: os.lstat(path).st_rdev", self.path) s = self.conn.reval("lambda path: os.lstat(path).st_rdev", self.path)
return (s >> 8, s & 0xff) 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): def chmod(self, permissions):
"""Wrapper around os.chmod""" """Wrapper around os.chmod"""
self.conn.os.chmod(self.path, permissions) self.conn.os.chmod(self.path, permissions)
...@@ -843,7 +837,9 @@ class RPath(RORPath): ...@@ -843,7 +837,9 @@ class RPath(RORPath):
def getincbase_str(self): def getincbase_str(self):
"""Return the base filename string of an increment file""" """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): def makedev(self, type, major, minor):
"""Make a special file with specified type, and major/minor nums""" """Make a special file with specified type, and major/minor nums"""
......
...@@ -80,19 +80,12 @@ class Select: ...@@ -80,19 +80,12 @@ class Select:
# This re should not match normal filenames, but usually just globs # This re should not match normal filenames, but usually just globs
glob_re = re.compile("(.*[*?[]|ignorecase\\:)", re.I | re.S) glob_re = re.compile("(.*[*?[]|ignorecase\\:)", re.I | re.S)
def __init__(self, rootrp, quoted_filenames = None): def __init__(self, rootrp):
"""Select initializer. rpath is the root directory """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.
"""
assert isinstance(rootrp, rpath.RPath) assert isinstance(rootrp, rpath.RPath)
self.selection_functions = [] self.selection_functions = []
self.rpath = rootrp self.rpath = rootrp
self.prefix = self.rpath.path self.prefix = self.rpath.path
self.quoting_on = Globals.quoting_enabled and quoted_filenames
def set_iter(self, iterate_parents = None, sel_func = None): def set_iter(self, iterate_parents = None, sel_func = None):
"""Initialize more variables, get ready to iterate """Initialize more variables, get ready to iterate
...@@ -103,9 +96,7 @@ class Select: ...@@ -103,9 +96,7 @@ class Select:
""" """
if not sel_func: sel_func = self.Select if not sel_func: sel_func = self.Select
self.rpath.setdata() # this may have changed since Select init self.rpath.setdata() # this may have changed since Select init
if self.quoting_on: self.iter = self.Iterate_fast(self.rpath, sel_func)
self.iter = self.Iterate(self.rpath, self.Iterate, sel_func)
else: self.iter = self.Iterate_fast(self.rpath, sel_func)
# only iterate parents if we are not starting from beginning # only iterate parents if we are not starting from beginning
self.next = self.iter.next self.next = self.iter.next
...@@ -113,12 +104,7 @@ class Select: ...@@ -113,12 +104,7 @@ class Select:
return self return self
def Iterate_fast(self, rpath, sel_func): def Iterate_fast(self, rpath, sel_func):
"""Like Iterate, but don't recur, saving time """Like Iterate, but don't recur, saving time"""
Only handles standard case (quoting off, starting from
beginning).
"""
def error_handler(exc, filename): def error_handler(exc, filename):
Log("Error initializing file %s/%s" % (rpath.path, filename), 2) Log("Error initializing file %s/%s" % (rpath.path, filename), 2)
return None return None
...@@ -194,17 +180,12 @@ class Select: ...@@ -194,17 +180,12 @@ class Select:
Log("Error initializing file %s/%s" % (rpath.path, filename), 2) Log("Error initializing file %s/%s" % (rpath.path, filename), 2)
return None return None
if self.quoting_on: for filename in robust.listrp(rpath):
for subdir in get_quoted_dir_children(rpath): new_rp = robust.check_common_error(
for rp in rec_func(subdir, rec_func, sel_func): error_handler, rpath.append, [filename])
if new_rp:
for rp in rec_func(new_rp, rec_func, sel_func):
yield rp 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): def Select(self, rp):
"""Run through the selection functions and return dominant val 0/1/2""" """Run through the selection functions and return dominant val 0/1/2"""
...@@ -617,22 +598,4 @@ probably isn't what you meant.""" % ...@@ -617,22 +598,4 @@ probably isn't what you meant.""" %
return res 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
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()
...@@ -5,7 +5,7 @@ from rdiff_backup.rpath import RPath ...@@ -5,7 +5,7 @@ from rdiff_backup.rpath import RPath
from rdiff_backup import Globals, Hardlink, SetConnections, Main, \ from rdiff_backup import Globals, Hardlink, SetConnections, Main, \
selection, lazy, Time, rpath selection, lazy, Time, rpath
SourceDir = "../src" SourceDir = "../rdiff_backup"
AbsCurdir = os.getcwd() # Absolute path name of current directory AbsCurdir = os.getcwd() # Absolute path name of current directory
AbsTFdir = AbsCurdir+"/testfiles" AbsTFdir = AbsCurdir+"/testfiles"
MiscDir = "../misc" MiscDir = "../misc"
......
import unittest, os, re, sys, time import unittest, os, re, sys, time
from commontest import * from commontest import *
from rdiff_backup import Globals, log, rpath, robust from rdiff_backup import Globals, log, rpath, robust, FilenameMapping
"""Regression tests""" """Regression tests"""
...@@ -43,7 +43,7 @@ class PathSetter(unittest.TestCase): ...@@ -43,7 +43,7 @@ class PathSetter(unittest.TestCase):
def reset_schema(self): def reset_schema(self):
self.rb_schema = SourceDir + \ 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): def refresh(self, *rp_list):
"""Reread data for the given rps""" """Reread data for the given rps"""
...@@ -60,20 +60,21 @@ class PathSetter(unittest.TestCase): ...@@ -60,20 +60,21 @@ class PathSetter(unittest.TestCase):
def exec_rb(self, time, *args): def exec_rb(self, time, *args):
"""Run rdiff-backup on given arguments""" """Run rdiff-backup on given arguments"""
arglist = [] 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]) arglist.append(self.src_prefix + args[0])
if len(args) > 1: if len(args) > 1:
arglist.append(self.dest_prefix + args[1]) arglist.append(self.dest_prefix + args[1])
assert len(args) == 2 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 print "executing " + cmdstr
assert not os.system(cmdstr) assert not os.system(cmdstr)
def exec_rb_extra_args(self, time, extra_args, *args): def exec_rb_extra_args(self, time, extra_args, *args):
"""Run rdiff-backup on given arguments""" """Run rdiff-backup on given arguments"""
arglist = [] 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]) arglist.append(self.src_prefix + args[0])
if len(args) > 1: if len(args) > 1:
arglist.append(self.dest_prefix + args[1]) arglist.append(self.dest_prefix + args[1])
...@@ -166,14 +167,19 @@ class PathSetter(unittest.TestCase): ...@@ -166,14 +167,19 @@ class PathSetter(unittest.TestCase):
"testfiles/output/rdiff-backup-data/increments/nochange")) "testfiles/output/rdiff-backup-data/increments/nochange"))
assert nochange_incs == 1 or nochange_incs == 0, nochange_incs 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""" """Return increment.______.dir paths"""
dirrp = rpath.RPath(Globals.local_connection, directory) if quoted:
incfiles = [filename for filename in robust.listrp(dirrp) FilenameMapping.set_init_quote_vals()
if filename.startswith(basename)] dirrp = FilenameMapping.QuotedRPath(Globals.local_connection,
incfiles.sort() directory)
incrps = map(lambda f: rpath.RPath(lc, directory+"/"+f), incfiles) else: dirrp = rpath.RPath(Globals.local_connection, directory)
return map(lambda x: x.path, filter(rpath.RPath.isincfile, incrps)) 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): class Final(PathSetter):
...@@ -204,7 +210,7 @@ class Final(PathSetter): ...@@ -204,7 +210,7 @@ class Final(PathSetter):
self.exec_rb(None, '../../../../../../proc', 'testfiles/procoutput') self.exec_rb(None, '../../../../../../proc', 'testfiles/procoutput')
def testProcRemote(self): def testProcRemote(self):
"""Test mirroring proc""" """Test mirroring proc remote"""
Myrm("testfiles/procoutput") Myrm("testfiles/procoutput")
self.set_connections(None, None, "test2/tmp/", "../../") self.set_connections(None, None, "test2/tmp/", "../../")
self.exec_rb(None, '../../../../../../proc', 'testfiles/procoutput') self.exec_rb(None, '../../../../../../proc', 'testfiles/procoutput')
...@@ -226,12 +232,13 @@ class Final(PathSetter): ...@@ -226,12 +232,13 @@ class Final(PathSetter):
Globals.time_separator = "_" Globals.time_separator = "_"
inc_paths = self.getinc_paths("increments.", inc_paths = self.getinc_paths("increments.",
"testfiles/output/rdiff-backup-data") "testfiles/output/rdiff-backup-data", 1)
Globals.time_separator = ":" Globals.time_separator = ":"
assert len(inc_paths) == 1 assert len(inc_paths) == 1, inc_paths
# Restore increment2 # Restore increment2
self.exec_rb(None, inc_paths[0], 'testfiles/restoretarget2') 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 # Now check to make sure no ":" in output directory
popen_fp = os.popen("find testfiles/output -name '*:*' | wc") popen_fp = os.popen("find testfiles/output -name '*:*' | wc")
...@@ -242,6 +249,10 @@ class Final(PathSetter): ...@@ -242,6 +249,10 @@ class Final(PathSetter):
class FinalSelection(PathSetter): class FinalSelection(PathSetter):
"""Test selection options""" """Test selection options"""
def run(cmd):
print "Executing: ", cmd
assert not os.system(cmd)
def testSelLocal(self): def testSelLocal(self):
"""Quick backup testing a few selection options""" """Quick backup testing a few selection options"""
self.delete_tmpdirs() self.delete_tmpdirs()
...@@ -277,11 +288,11 @@ testfiles/increment2/changed_dir""") ...@@ -277,11 +288,11 @@ testfiles/increment2/changed_dir""")
# Test selective restoring # Test selective restoring
mirror_rp = rpath.RPath(Globals.local_connection, "testfiles/output") mirror_rp = rpath.RPath(Globals.local_connection, "testfiles/output")
restore_filename = get_increment_rp(mirror_rp, 10000).path restore_filename = get_increment_rp(mirror_rp, 10000).path
assert not os.system(self.rb_schema + self.run(self.rb_schema +
"--include testfiles/restoretarget1/various_file_types/" "--include testfiles/restoretarget1/various_file_types/"
"regular_file " "regular_file "
"--exclude '**' " + "--exclude '**' " +
restore_filename + " testfiles/restoretarget1") restore_filename + " testfiles/restoretarget1")
assert os.lstat("testfiles/restoretarget1/various_file_types/" assert os.lstat("testfiles/restoretarget1/various_file_types/"
"regular_file") "regular_file")
self.assertRaises(OSError, os.lstat, "testfiles/restoretarget1/tester") self.assertRaises(OSError, os.lstat, "testfiles/restoretarget1/tester")
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment