Commit 6e62b4c4 authored by ben's avatar ben

Bug fixes to resuming and error correction code


git-svn-id: http://svn.savannah.nongnu.org/svn/rdiff-backup@112 2b77aa54-bcbc-44c9-a7ec-4f6cf2b41109
parent 35fae838
...@@ -189,7 +189,7 @@ class DSRPath(RPath): ...@@ -189,7 +189,7 @@ class DSRPath(RPath):
return self.__class__(self.source, self.conn, self.base, index) return self.__class__(self.source, self.conn, self.base, index)
class DestructiveSteppingFinalizer(IterTreeReducer): class DestructiveSteppingFinalizer(ErrorITR):
"""Finalizer that can work on an iterator of dsrpaths """Finalizer that can work on an iterator of dsrpaths
The reason we have to use an IterTreeReducer is that some files The reason we have to use an IterTreeReducer is that some files
...@@ -203,11 +203,6 @@ class DestructiveSteppingFinalizer(IterTreeReducer): ...@@ -203,11 +203,6 @@ class DestructiveSteppingFinalizer(IterTreeReducer):
self.dsrpath = dsrpath self.dsrpath = dsrpath
def end_process(self): def end_process(self):
if self.dsrpath: if self.dsrpath: self.dsrpath.write_changes()
Robust.check_common_error(self.dsrpath.write_changes,
lambda exc: Log("Error %s finalizing file %s" %
(str(exc), dsrp.path)))
#!/usr/bin/env python #!/usr/bin/env python
# #
# rdiff-backup -- Mirror files while keeping incremental changes # rdiff-backup -- Mirror files while keeping incremental changes
# Version 0.7.5.3 released May 25, 2002 # Version 0.7.5.4 released May 29, 2002
# Copyright (C) 2001, 2002 Ben Escoto <bescoto@stanford.edu> # Copyright (C) 2001, 2002 Ben Escoto <bescoto@stanford.edu>
# #
# This program is licensed under the GNU General Public License (GPL). # This program is licensed under the GNU General Public License (GPL).
......
...@@ -94,15 +94,18 @@ class HLSourceStruct: ...@@ -94,15 +94,18 @@ class HLSourceStruct:
""" """
collated = RORPIter.CollateIterators(cls.initial_dsiter2, sigiter) collated = RORPIter.CollateIterators(cls.initial_dsiter2, sigiter)
finalizer = DestructiveSteppingFinalizer() finalizer = DestructiveSteppingFinalizer()
def error_handler(exc, dest_sig, dsrp):
Log("Error %s producing a diff of %s" %
(exc, dsrp and dsrp.path), 2)
return None
def diffs(): def diffs():
for dsrp, dest_sig in collated: for dsrp, dest_sig in collated:
if dest_sig: if dest_sig:
if dest_sig.isplaceholder(): yield dest_sig if dest_sig.isplaceholder(): yield dest_sig
else: else:
diff = Robust.check_common_error( diff = Robust.check_common_error(
lambda: RORPIter.diffonce(dest_sig, dsrp), error_handler, RORPIter.diffonce, dest_sig, dsrp)
lambda exc: Log("Error %s producing a diff of %s" %
(str(exc), dsrp and dsrp.path), 2))
if diff: yield diff if diff: yield diff
if dsrp: finalizer(dsrp.index, dsrp) if dsrp: finalizer(dsrp.index, dsrp)
finalizer.Finish() finalizer.Finish()
...@@ -216,24 +219,24 @@ class HLDestinationStruct: ...@@ -216,24 +219,24 @@ class HLDestinationStruct:
"""Apply diffs and finalize""" """Apply diffs and finalize"""
collated = RORPIter.CollateIterators(diffs, cls.initial_dsiter2) collated = RORPIter.CollateIterators(diffs, cls.initial_dsiter2)
finalizer = cls.get_finalizer() finalizer = cls.get_finalizer()
dsrp = None diff_rorp, dsrp = None, None
def error_checked(): def patch(diff_rorp, dsrp):
"""Inner writing loop, check this for errors"""
indexed_tuple = collated.next()
Log("Processing %s" % str(indexed_tuple), 7)
diff_rorp, dsrp = indexed_tuple
if not dsrp: dsrp = cls.get_dsrp(dest_rpath, diff_rorp.index) if not dsrp: dsrp = cls.get_dsrp(dest_rpath, diff_rorp.index)
if diff_rorp and not diff_rorp.isplaceholder(): if diff_rorp and not diff_rorp.isplaceholder():
RORPIter.patchonce_action(None, dsrp, diff_rorp).execute() RORPIter.patchonce_action(None, dsrp, diff_rorp).execute()
finalizer(dsrp.index, dsrp)
return dsrp return dsrp
try: def error_handler(exc, diff_rorp, dsrp):
while 1: filename = dsrp and dsrp.path or os.path.join(*diff_rorp.index)
try: dsrp = cls.check_skip_error(error_checked, dsrp) Log("Error: %s processing file %s" % (exc, filename), 2)
except StopIteration: break
except: Log.exception(1) for indexed_tuple in collated:
Log("Processing %s" % str(indexed_tuple), 7)
diff_rorp, dsrp = indexed_tuple
dsrp = Robust.check_common_error(error_handler, patch,
diff_rorp, dsrp)
finalizer(dsrp.index, dsrp)
finalizer.Finish() finalizer.Finish()
def patch_w_datadir_writes(cls, dest_rpath, diffs, inc_rpath): def patch_w_datadir_writes(cls, dest_rpath, diffs, inc_rpath):
...@@ -243,25 +246,19 @@ class HLDestinationStruct: ...@@ -243,25 +246,19 @@ class HLDestinationStruct:
Stats.open_dir_stats_file() Stats.open_dir_stats_file()
dsrp = None dsrp = None
def error_checked():
"""Inner writing loop, check this for errors"""
indexed_tuple = collated.next()
Log("Processing %s" % str(indexed_tuple), 7)
diff_rorp, dsrp = indexed_tuple
if not dsrp: dsrp = cls.get_dsrp(dest_rpath, diff_rorp.index)
if diff_rorp and diff_rorp.isplaceholder(): diff_rorp = None
ITR(dsrp.index, diff_rorp, dsrp)
finalizer(dsrp.index, dsrp)
return dsrp
try: try:
while 1: for indexed_tuple in collated:
try: dsrp = cls.check_skip_error(error_checked, dsrp) Log("Processing %s" % str(indexed_tuple), 7)
except StopIteration: break diff_rorp, dsrp = indexed_tuple
if not dsrp: dsrp = cls.get_dsrp(dest_rpath, diff_rorp.index)
if diff_rorp and diff_rorp.isplaceholder(): diff_rorp = None
ITR(dsrp.index, diff_rorp, dsrp)
finalizer(dsrp.index, dsrp)
SaveState.checkpoint(ITR, finalizer, dsrp) SaveState.checkpoint(ITR, finalizer, dsrp)
cls.check_skip_error(ITR.Finish, dsrp) ITR.Finish()
cls.check_skip_error(finalizer.Finish, dsrp) finalizer.Finish()
except: cls.handle_last_error(dsrp, finalizer, ITR) except: cls.handle_last_error(dsrp, finalizer, ITR)
if Globals.preserve_hardlinks: Hardlink.final_writedata() if Globals.preserve_hardlinks: Hardlink.final_writedata()
Stats.close_dir_stats_file() Stats.close_dir_stats_file()
Stats.write_session_statistics(ITR) Stats.write_session_statistics(ITR)
...@@ -274,54 +271,29 @@ class HLDestinationStruct: ...@@ -274,54 +271,29 @@ class HLDestinationStruct:
Stats.open_dir_stats_file() Stats.open_dir_stats_file()
dsrp = None dsrp = None
def error_checked():
"""Inner writing loop, catch variety of errors from this"""
indexed_tuple = collated.next()
Log("Processing %s" % str(indexed_tuple), 7)
diff_rorp, dsrp = indexed_tuple
index = indexed_tuple.index
if not dsrp: dsrp = cls.get_dsrp(dest_rpath, index)
if diff_rorp and diff_rorp.isplaceholder(): diff_rorp = None
ITR(index, diff_rorp, dsrp)
finalizer(index, dsrp)
return dsrp
try: try:
while 1: for indexed_tuple in collated:
try: dsrp = cls.check_skip_error(error_checked, dsrp) Log("Processing %s" % str(indexed_tuple), 7)
except StopIteration: break diff_rorp, dsrp = indexed_tuple
index = indexed_tuple.index
if not dsrp: dsrp = cls.get_dsrp(dest_rpath, index)
if diff_rorp and diff_rorp.isplaceholder(): diff_rorp = None
ITR(index, diff_rorp, dsrp)
finalizer(index, dsrp)
SaveState.checkpoint(ITR, finalizer, dsrp) SaveState.checkpoint(ITR, finalizer, dsrp)
cls.check_skip_error(ITR.Finish, dsrp) ITR.Finish()
cls.check_skip_error(finalizer.Finish, dsrp) finalizer.Finish()
except: cls.handle_last_error(dsrp, finalizer, ITR) except: cls.handle_last_error(dsrp, finalizer, ITR)
if Globals.preserve_hardlinks: Hardlink.final_writedata() if Globals.preserve_hardlinks: Hardlink.final_writedata()
Stats.close_dir_stats_file() Stats.close_dir_stats_file()
Stats.write_session_statistics(ITR) Stats.write_session_statistics(ITR)
SaveState.checkpoint_remove() SaveState.checkpoint_remove()
def check_skip_error(cls, thunk, dsrp):
"""Run thunk, catch certain errors skip files"""
try: return thunk()
except (EnvironmentError, SkipFileException, DSRPPermError,
RPathException), exc:
if (not isinstance(exc, EnvironmentError) or
(errno.errorcode[exc[0]] in
['EPERM', 'ENOENT', 'EACCES', 'EBUSY', 'EEXIST',
'ENOTDIR', 'ENAMETOOLONG', 'EINTR', 'ENOTEMPTY',
'EIO', # reported by docv
'ETXTBSY' # reported by Campbell on some NT system
])):
Log.exception()
Log("Skipping file because of error after %s" %
(dsrp and dsrp.index,), 2)
return None
else:
Log.exception(1,2)
raise
def handle_last_error(cls, dsrp, finalizer, ITR): def handle_last_error(cls, dsrp, finalizer, ITR):
"""If catch fatal error, try to checkpoint before exiting""" """If catch fatal error, try to checkpoint before exiting"""
Log.exception(1) Log.exception(1, 2)
TracebackArchive.log()
SaveState.checkpoint(ITR, finalizer, dsrp, 1) SaveState.checkpoint(ITR, finalizer, dsrp, 1)
if Globals.preserve_hardlinks: Hardlink.final_checkpoint(Globals.rbdir) if Globals.preserve_hardlinks: Hardlink.final_checkpoint(Globals.rbdir)
SaveState.touch_last_file_definitive() SaveState.touch_last_file_definitive()
......
import traceback
execfile("statistics.py") execfile("statistics.py")
####################################################################### #######################################################################
...@@ -121,7 +122,7 @@ class Inc: ...@@ -121,7 +122,7 @@ class Inc:
MakeStatic(Inc) MakeStatic(Inc)
class IncrementITR(StatsITR): class IncrementITR(ErrorITR, StatsITR):
"""Patch and increment mirror directory """Patch and increment mirror directory
This has to be an ITR because directories that have files in them This has to be an ITR because directories that have files in them
...@@ -236,13 +237,8 @@ class IncrementITR(StatsITR): ...@@ -236,13 +237,8 @@ class IncrementITR(StatsITR):
def end_process(self): def end_process(self):
"""Do final work when leaving a tree (directory)""" """Do final work when leaving a tree (directory)"""
try: diff_rorp, dsrp, incpref = self.diff_rorp, self.dsrp, self.incpref diff_rorp, dsrp, incpref = self.diff_rorp, self.dsrp, self.incpref
except AttributeError: # This weren't set because of some error if self.mirror_isdirectory and (diff_rorp or self.changed):
return
if self.mirror_isdirectory:
if not diff_rorp and not self.changed: return
if self.directory_replacement: if self.directory_replacement:
tf = self.directory_replacement tf = self.directory_replacement
self.incrp = Robust.chain( self.incrp = Robust.chain(
...@@ -264,7 +260,7 @@ class IncrementITR(StatsITR): ...@@ -264,7 +260,7 @@ class IncrementITR(StatsITR):
self.add_file_stats(subinstance) self.add_file_stats(subinstance)
class MirrorITR(StatsITR): class MirrorITR(ErrorITR, StatsITR):
"""Like IncrementITR, but only patch mirror directory, don't increment""" """Like IncrementITR, but only patch mirror directory, don't increment"""
# This is always None since no increments will be created # This is always None since no increments will be created
incrp = None incrp = None
...@@ -284,13 +280,9 @@ class MirrorITR(StatsITR): ...@@ -284,13 +280,9 @@ class MirrorITR(StatsITR):
def end_process(self): def end_process(self):
"""Update statistics when leaving""" """Update statistics when leaving"""
try: diff_rorp, mirror_dsrp = self.diff_rorp, self.mirror_dsrp self.end_stats(self.diff_rorp, self.mirror_dsrp)
except AttributeError: # Some error above prevented these being set if self.mirror_dsrp.isdir():
return Stats.write_dir_stats_line(self, self.mirror_dsrp.index)
self.end_stats(diff_rorp, mirror_dsrp)
if mirror_dsrp.isdir():
Stats.write_dir_stats_line(self, mirror_dsrp.index)
def branch_process(self, subinstance): def branch_process(self, subinstance):
"""Update statistics with subdirectory results""" """Update statistics with subdirectory results"""
......
...@@ -196,9 +196,9 @@ class IterTreeReducer: ...@@ -196,9 +196,9 @@ class IterTreeReducer:
iterator nature of the connection between hosts and the temporal iterator nature of the connection between hosts and the temporal
order in which the files are processed. order in which the files are processed.
There are three stub functions below: start_process, end_process, There are four stub functions below: start_process, end_process,
and branch_process. A class that subclasses this one should fill branch_process, and check_for_errors. A class that subclasses
in these functions with real values. this one will probably fill in these functions to do more.
It is important that this class be pickable, so keep that in mind It is important that this class be pickable, so keep that in mind
when subclassing (this is used to resume failed sessions). when subclassing (this is used to resume failed sessions).
...@@ -210,6 +210,7 @@ class IterTreeReducer: ...@@ -210,6 +210,7 @@ class IterTreeReducer:
self.index = None self.index = None
self.subinstance = None self.subinstance = None
self.finished = None self.finished = None
self.caught_exception, self.start_successful = None, None
def intree(self, index): def intree(self, index):
"""Return true if index is still in current tree""" """Return true if index is still in current tree"""
...@@ -239,14 +240,33 @@ class IterTreeReducer: ...@@ -239,14 +240,33 @@ class IterTreeReducer:
"""Process a branch right after it is finished (stub)""" """Process a branch right after it is finished (stub)"""
pass pass
def check_for_errors(self, function, *args):
"""start/end_process is called by this function
Usually it will distinguish between two types of errors. Some
are serious and will be reraised, others are caught and simply
invalidate the current instance by setting
self.caught_exception.
"""
try: return apply(function, args)
except: raise
def Finish(self): def Finish(self):
"""Call at end of sequence to tie everything up""" """Call at end of sequence to tie everything up"""
assert not self.finished, (self.base_index, self.index) if not self.start_successful or self.finished:
if self.subinstance: self.caught_exception = 1
self.subinstance.Finish() if self.caught_exception: self.log_prev_error(self.index)
self.branch_process(self.subinstance) else:
self.end_process() if self.subinstance:
self.finished = 1 self.subinstance.Finish()
self.branch_process(self.subinstance)
self.check_for_errors(self.end_process)
self.finished = 1
def log_prev_error(self, index):
"""Call function if no pending exception"""
Log("Skipping %s because of previous error" % os.path.join(*index), 2)
def __call__(self, *args): def __call__(self, *args):
"""Process args, where args[0] is current position in iterator """Process args, where args[0] is current position in iterator
...@@ -263,18 +283,37 @@ class IterTreeReducer: ...@@ -263,18 +283,37 @@ class IterTreeReducer:
assert type(index) is types.TupleType, type(index) assert type(index) is types.TupleType, type(index)
if self.index is None: if self.index is None:
self.start_process(*args) self.check_for_errors(self.start_process, *args)
self.start_successful = 1
self.index = self.base_index = index self.index = self.base_index = index
return 1 return 1
if index <= self.index: if index <= self.index:
Log("Warning: oldindex %s >= newindex %s" % (self.index, index), 2) Log("Warning: oldindex %s >= newindex %s" % (self.index, index), 2)
return 1
if not self.intree(index): if not self.intree(index):
self.Finish() self.Finish()
return None return None
else:
self.process_w_subinstance(args) if self.caught_exception: self.log_prev_error(index)
self.index = index else: self.process_w_subinstance(args)
return 1 self.index = index
return 1
class ErrorITR(IterTreeReducer):
"""Adds some error handling to above ITR, if ITR processes files"""
def on_error(self, exc, *args):
"""This is run on any exception in start/end-process"""
self.caught_exception = 1
if args and isinstance(args[0], tuple):
filename = os.path.join(*args[0])
elif self.index: filename = os.path.join(*self.index)
else: filename = "."
Log("Error '%s' processing %s" % (exc, filename), 2)
def check_for_errors(self, function, *args):
"""Catch some non-fatal errors"""
return Robust.check_common_error(self.on_error, function, *args)
import time, sys import time, sys, traceback
execfile("lazy.py") execfile("lazy.py")
####################################################################### #######################################################################
...@@ -121,7 +121,13 @@ class Logger: ...@@ -121,7 +121,13 @@ class Logger:
Globals.Main.cleanup() Globals.Main.cleanup()
sys.exit(1) sys.exit(1)
def exception(self, only_terminal = 0, verbosity = 4): def exception_to_string(self):
"""Return string version of current exception"""
type, value, tb = sys.exc_info()
return ("Exception '%s' raised of class '%s':\n%s" %
(value, type, "".join(traceback.format_tb(tb))))
def exception(self, only_terminal = 0, verbosity = 5):
"""Log an exception and traceback """Log an exception and traceback
If only_terminal is None, log normally. If it is 1, then only If only_terminal is None, log normally. If it is 1, then only
...@@ -135,9 +141,6 @@ class Logger: ...@@ -135,9 +141,6 @@ class Logger:
logging_func = self.__call__ logging_func = self.__call__
else: logging_func = self.log_to_term else: logging_func = self.log_to_term
exc_info = sys.exc_info() logging_func(self.exception_to_string(), verbosity)
logging_func("Exception %s raised of class %s" %
(exc_info[1], exc_info[0]), verbosity)
logging_func("".join(traceback.format_tb(exc_info[2])), verbosity+1)
Log = Logger() Log = Logger()
...@@ -68,6 +68,7 @@ class RobustAction: ...@@ -68,6 +68,7 @@ class RobustAction:
return self.final_func(init_val) return self.final_func(init_val)
except Exception, exc: # Catch all errors except Exception, exc: # Catch all errors
Log.exception() Log.exception()
TracebackArchive.add()
if ran_init_thunk: self.error_handler(exc, 1, init_val) if ran_init_thunk: self.error_handler(exc, 1, init_val)
else: self.error_handler(exc, None, None) else: self.error_handler(exc, None, None)
raise exc raise exc
...@@ -233,41 +234,59 @@ class Robust: ...@@ -233,41 +234,59 @@ class Robust:
tf.setdata() tf.setdata()
return Robust.make_tf_robustaction(init, tf, rp) return Robust.make_tf_robustaction(init, tf, rp)
def check_common_error(init_thunk, error_thunk = lambda exc: None): def check_common_error(error_handler, function, *args):
"""Execute init_thunk, if error, run error_thunk on exception """Apply function to args, if error, run error_handler on exception
This only catches certain exceptions which seems innocent This only catches certain exceptions which seems innocent
enough. enough.
""" """
try: return init_thunk() try: return function(*args)
except (EnvironmentError, SkipFileException, DSRPPermError, except (EnvironmentError, SkipFileException, DSRPPermError,
RPathException, RdiffException), exc: RPathException, RdiffException), exc:
TracebackArchive.add()
if (not isinstance(exc, EnvironmentError) or if (not isinstance(exc, EnvironmentError) or
(errno.errorcode[exc[0]] in (errno.errorcode[exc[0]] in
['EPERM', 'ENOENT', 'EACCES', 'EBUSY', 'EEXIST', ['EPERM', 'ENOENT', 'EACCES', 'EBUSY', 'EEXIST',
'ENOTDIR', 'ENAMETOOLONG', 'EINTR', 'ENOTEMPTY', 'ENOTDIR', 'ENAMETOOLONG', 'EINTR', 'ENOTEMPTY',
'EIO', # reported by docv 'EIO', 'ETXTBSY', 'ESRCH', 'EINVAL'])):
'ETXTBSY' # reported by Campbell on some NT system
])):
Log.exception() Log.exception()
return error_thunk(exc) if error_handler: return error_handler(exc, *args)
else: else:
Log.exception(1, 2) Log.exception(1, 2)
raise raise
def listrp(rp): def listrp(rp):
"""Like rp.listdir() but return [] if error, and sort results""" """Like rp.listdir() but return [] if error, and sort results"""
def error_thunk(exc): def error_handler(exc):
Log("Error listing directory %s" % rp.path, 2) Log("Error listing directory %s" % rp.path, 2)
return [] return []
dir_listing = Robust.check_common_error(rp.listdir, error_thunk) dir_listing = Robust.check_common_error(error_handler, rp.listdir)
dir_listing.sort() dir_listing.sort()
return dir_listing return dir_listing
MakeStatic(Robust) MakeStatic(Robust)
class TracebackArchive:
"""Save last 10 caught exceptions, so they can be printed if fatal"""
_traceback_strings = []
def add(cls):
"""Add most recent exception to archived list"""
cls._traceback_strings.append(Log.exception_to_string())
if len(cls._traceback_strings) > 10:
cls._traceback_strings = cls._traceback_strings[:10]
def log(cls):
"""Print all exception information to log file"""
if cls._traceback_strings:
Log("------------ Old traceback info -----------\n%s"
"-------------------------------------------" %
("\n".join(cls._traceback_strings),), 3)
MakeClass(TracebackArchive)
class TempFileManager: class TempFileManager:
"""Manage temp files""" """Manage temp files"""
......
...@@ -57,6 +57,10 @@ class RORPIter: ...@@ -57,6 +57,10 @@ class RORPIter:
def Signatures(rp_iter): def Signatures(rp_iter):
"""Yield signatures of rpaths in given rp_iter""" """Yield signatures of rpaths in given rp_iter"""
def error_handler(exc, rp):
Log("Error generating signature for %s" % rp.path)
return None
for rp in rp_iter: for rp in rp_iter:
if rp.isplaceholder(): yield rp if rp.isplaceholder(): yield rp
else: else:
...@@ -65,11 +69,9 @@ class RORPIter: ...@@ -65,11 +69,9 @@ class RORPIter:
if rp.isflaglinked(): rorp.flaglinked() if rp.isflaglinked(): rorp.flaglinked()
else: else:
fp = Robust.check_common_error( fp = Robust.check_common_error(
lambda: Rdiff.get_signature(rp)) error_handler, Rdiff.get_signature, rp)
if fp: rorp.setfile(fp) if fp: rorp.setfile(fp)
else: else: continue
Log("Error generating signature for %s" % rp.path)
continue
yield rorp yield rorp
def GetSignatureIter(base_rp): def GetSignatureIter(base_rp):
......
...@@ -91,10 +91,10 @@ class Select: ...@@ -91,10 +91,10 @@ class Select:
self.starting_index = starting_index self.starting_index = starting_index
self.iter = self.iterate_starting_from(self.dsrpath, self.iter = self.iterate_starting_from(self.dsrpath,
self.iterate_starting_from, sel_func) self.iterate_starting_from, sel_func)
else: else: self.iter = self.Iterate(self.dsrpath, self.Iterate, sel_func)
assert not iterate_parents
self.iter = self.Iterate(self.dsrpath, self.Iterate, sel_func) # only iterate parents if we are not starting from beginning
self.iterate_parents = iterate_parents self.iterate_parents = starting_index is not None and iterate_parents
self.next = self.iter.next self.next = self.iter.next
self.__iter__ = lambda: self self.__iter__ = lambda: self
return self return self
...@@ -127,17 +127,18 @@ class Select: ...@@ -127,17 +127,18 @@ class Select:
def iterate_in_dir(self, dsrpath, rec_func, sel_func): def iterate_in_dir(self, dsrpath, rec_func, sel_func):
"""Iterate the dsrps in directory dsrpath.""" """Iterate the dsrps in directory dsrpath."""
def error_handler(exc, filename):
Log("Error initializing file %s/%s" % (dsrpath.path, filename), 2)
return None
if self.quoting_on: if self.quoting_on:
for subdir in FilenameMapping.get_quoted_dir_children(dsrpath): for subdir in FilenameMapping.get_quoted_dir_children(dsrpath):
for dsrp in rec_func(subdir, rec_func, sel_func): yield dsrp for dsrp in rec_func(subdir, rec_func, sel_func): yield dsrp
else: else:
for filename in Robust.listrp(dsrpath): for filename in Robust.listrp(dsrpath):
new_dsrp = Robust.check_common_error( new_dsrp = Robust.check_common_error(
lambda: dsrpath.append(filename)) error_handler, dsrpath.append, filename)
if not new_dsrp: if new_dsrp:
Log("Error initializing file %s/%s" %
(dsrpath.path, filename), 2)
else:
for dsrp in rec_func(new_dsrp, rec_func, sel_func): for dsrp in rec_func(new_dsrp, rec_func, sel_func):
yield dsrp yield dsrp
...@@ -208,7 +209,6 @@ class Select: ...@@ -208,7 +209,6 @@ class Select:
self.parse_last_excludes() self.parse_last_excludes()
self.parse_rbdir_exclude() self.parse_rbdir_exclude()
self.parse_proc_exclude()
def parse_catch_error(self, exc): def parse_catch_error(self, exc):
"""Deal with selection error exc""" """Deal with selection error exc"""
...@@ -231,11 +231,6 @@ pattern (such as '**') which matches the base directory.""" % ...@@ -231,11 +231,6 @@ pattern (such as '**') which matches the base directory.""" %
self.add_selection_func( self.add_selection_func(
self.glob_get_tuple_sf(("rdiff-backup-data",), 0), 1) self.glob_get_tuple_sf(("rdiff-backup-data",), 0), 1)
def parse_proc_exclude(self):
"""Exclude the /proc directory if starting from /"""
if self.prefix == "/":
self.add_selection_func(self.glob_get_tuple_sf(("proc",), 0), 1)
def parse_last_excludes(self): def parse_last_excludes(self):
"""Exit with error if last selection function isn't an exclude""" """Exit with error if last selection function isn't an exclude"""
if (self.selection_functions and if (self.selection_functions and
......
...@@ -286,7 +286,8 @@ class Stats: ...@@ -286,7 +286,8 @@ class Stats:
"directory_statistics"), Time.curtime, suffix) "directory_statistics"), Time.curtime, suffix)
if cls._dir_stats_rp.lstat(): if cls._dir_stats_rp.lstat():
Log("Warning, statistics file %s already exists, appending", 2) Log("Warning, statistics file %s already exists, appending" %
cls._dir_stats_rp.path, 2)
cls._dir_stats_fp = cls._dir_stats_rp.open("ab", cls._dir_stats_fp = cls._dir_stats_rp.open("ab",
Globals.compression) Globals.compression)
else: cls._dir_stats_fp = \ else: cls._dir_stats_fp = \
......
...@@ -189,7 +189,7 @@ class DSRPath(RPath): ...@@ -189,7 +189,7 @@ class DSRPath(RPath):
return self.__class__(self.source, self.conn, self.base, index) return self.__class__(self.source, self.conn, self.base, index)
class DestructiveSteppingFinalizer(IterTreeReducer): class DestructiveSteppingFinalizer(ErrorITR):
"""Finalizer that can work on an iterator of dsrpaths """Finalizer that can work on an iterator of dsrpaths
The reason we have to use an IterTreeReducer is that some files The reason we have to use an IterTreeReducer is that some files
...@@ -203,11 +203,6 @@ class DestructiveSteppingFinalizer(IterTreeReducer): ...@@ -203,11 +203,6 @@ class DestructiveSteppingFinalizer(IterTreeReducer):
self.dsrpath = dsrpath self.dsrpath = dsrpath
def end_process(self): def end_process(self):
if self.dsrpath: if self.dsrpath: self.dsrpath.write_changes()
Robust.check_common_error(self.dsrpath.write_changes,
lambda exc: Log("Error %s finalizing file %s" %
(str(exc), dsrp.path)))
...@@ -8,7 +8,7 @@ import re, os ...@@ -8,7 +8,7 @@ import re, os
class Globals: class Globals:
# The current version of rdiff-backup # The current version of rdiff-backup
version = "0.7.5.3" version = "0.7.5.4"
# If this is set, use this value in seconds as the current time # If this is set, use this value in seconds as the current time
# instead of reading it from the clock. # instead of reading it from the clock.
......
#!/usr/bin/env python #!/usr/bin/env python
# #
# rdiff-backup -- Mirror files while keeping incremental changes # rdiff-backup -- Mirror files while keeping incremental changes
# Version 0.7.5.3 released May 25, 2002 # Version 0.7.5.4 released May 29, 2002
# Copyright (C) 2001, 2002 Ben Escoto <bescoto@stanford.edu> # Copyright (C) 2001, 2002 Ben Escoto <bescoto@stanford.edu>
# #
# This program is licensed under the GNU General Public License (GPL). # This program is licensed under the GNU General Public License (GPL).
......
...@@ -94,15 +94,18 @@ class HLSourceStruct: ...@@ -94,15 +94,18 @@ class HLSourceStruct:
""" """
collated = RORPIter.CollateIterators(cls.initial_dsiter2, sigiter) collated = RORPIter.CollateIterators(cls.initial_dsiter2, sigiter)
finalizer = DestructiveSteppingFinalizer() finalizer = DestructiveSteppingFinalizer()
def error_handler(exc, dest_sig, dsrp):
Log("Error %s producing a diff of %s" %
(exc, dsrp and dsrp.path), 2)
return None
def diffs(): def diffs():
for dsrp, dest_sig in collated: for dsrp, dest_sig in collated:
if dest_sig: if dest_sig:
if dest_sig.isplaceholder(): yield dest_sig if dest_sig.isplaceholder(): yield dest_sig
else: else:
diff = Robust.check_common_error( diff = Robust.check_common_error(
lambda: RORPIter.diffonce(dest_sig, dsrp), error_handler, RORPIter.diffonce, dest_sig, dsrp)
lambda exc: Log("Error %s producing a diff of %s" %
(str(exc), dsrp and dsrp.path), 2))
if diff: yield diff if diff: yield diff
if dsrp: finalizer(dsrp.index, dsrp) if dsrp: finalizer(dsrp.index, dsrp)
finalizer.Finish() finalizer.Finish()
...@@ -216,24 +219,24 @@ class HLDestinationStruct: ...@@ -216,24 +219,24 @@ class HLDestinationStruct:
"""Apply diffs and finalize""" """Apply diffs and finalize"""
collated = RORPIter.CollateIterators(diffs, cls.initial_dsiter2) collated = RORPIter.CollateIterators(diffs, cls.initial_dsiter2)
finalizer = cls.get_finalizer() finalizer = cls.get_finalizer()
dsrp = None diff_rorp, dsrp = None, None
def error_checked(): def patch(diff_rorp, dsrp):
"""Inner writing loop, check this for errors"""
indexed_tuple = collated.next()
Log("Processing %s" % str(indexed_tuple), 7)
diff_rorp, dsrp = indexed_tuple
if not dsrp: dsrp = cls.get_dsrp(dest_rpath, diff_rorp.index) if not dsrp: dsrp = cls.get_dsrp(dest_rpath, diff_rorp.index)
if diff_rorp and not diff_rorp.isplaceholder(): if diff_rorp and not diff_rorp.isplaceholder():
RORPIter.patchonce_action(None, dsrp, diff_rorp).execute() RORPIter.patchonce_action(None, dsrp, diff_rorp).execute()
finalizer(dsrp.index, dsrp)
return dsrp return dsrp
try: def error_handler(exc, diff_rorp, dsrp):
while 1: filename = dsrp and dsrp.path or os.path.join(*diff_rorp.index)
try: dsrp = cls.check_skip_error(error_checked, dsrp) Log("Error: %s processing file %s" % (exc, filename), 2)
except StopIteration: break
except: Log.exception(1) for indexed_tuple in collated:
Log("Processing %s" % str(indexed_tuple), 7)
diff_rorp, dsrp = indexed_tuple
dsrp = Robust.check_common_error(error_handler, patch,
diff_rorp, dsrp)
finalizer(dsrp.index, dsrp)
finalizer.Finish() finalizer.Finish()
def patch_w_datadir_writes(cls, dest_rpath, diffs, inc_rpath): def patch_w_datadir_writes(cls, dest_rpath, diffs, inc_rpath):
...@@ -243,25 +246,19 @@ class HLDestinationStruct: ...@@ -243,25 +246,19 @@ class HLDestinationStruct:
Stats.open_dir_stats_file() Stats.open_dir_stats_file()
dsrp = None dsrp = None
def error_checked():
"""Inner writing loop, check this for errors"""
indexed_tuple = collated.next()
Log("Processing %s" % str(indexed_tuple), 7)
diff_rorp, dsrp = indexed_tuple
if not dsrp: dsrp = cls.get_dsrp(dest_rpath, diff_rorp.index)
if diff_rorp and diff_rorp.isplaceholder(): diff_rorp = None
ITR(dsrp.index, diff_rorp, dsrp)
finalizer(dsrp.index, dsrp)
return dsrp
try: try:
while 1: for indexed_tuple in collated:
try: dsrp = cls.check_skip_error(error_checked, dsrp) Log("Processing %s" % str(indexed_tuple), 7)
except StopIteration: break diff_rorp, dsrp = indexed_tuple
if not dsrp: dsrp = cls.get_dsrp(dest_rpath, diff_rorp.index)
if diff_rorp and diff_rorp.isplaceholder(): diff_rorp = None
ITR(dsrp.index, diff_rorp, dsrp)
finalizer(dsrp.index, dsrp)
SaveState.checkpoint(ITR, finalizer, dsrp) SaveState.checkpoint(ITR, finalizer, dsrp)
cls.check_skip_error(ITR.Finish, dsrp) ITR.Finish()
cls.check_skip_error(finalizer.Finish, dsrp) finalizer.Finish()
except: cls.handle_last_error(dsrp, finalizer, ITR) except: cls.handle_last_error(dsrp, finalizer, ITR)
if Globals.preserve_hardlinks: Hardlink.final_writedata() if Globals.preserve_hardlinks: Hardlink.final_writedata()
Stats.close_dir_stats_file() Stats.close_dir_stats_file()
Stats.write_session_statistics(ITR) Stats.write_session_statistics(ITR)
...@@ -274,54 +271,29 @@ class HLDestinationStruct: ...@@ -274,54 +271,29 @@ class HLDestinationStruct:
Stats.open_dir_stats_file() Stats.open_dir_stats_file()
dsrp = None dsrp = None
def error_checked():
"""Inner writing loop, catch variety of errors from this"""
indexed_tuple = collated.next()
Log("Processing %s" % str(indexed_tuple), 7)
diff_rorp, dsrp = indexed_tuple
index = indexed_tuple.index
if not dsrp: dsrp = cls.get_dsrp(dest_rpath, index)
if diff_rorp and diff_rorp.isplaceholder(): diff_rorp = None
ITR(index, diff_rorp, dsrp)
finalizer(index, dsrp)
return dsrp
try: try:
while 1: for indexed_tuple in collated:
try: dsrp = cls.check_skip_error(error_checked, dsrp) Log("Processing %s" % str(indexed_tuple), 7)
except StopIteration: break diff_rorp, dsrp = indexed_tuple
index = indexed_tuple.index
if not dsrp: dsrp = cls.get_dsrp(dest_rpath, index)
if diff_rorp and diff_rorp.isplaceholder(): diff_rorp = None
ITR(index, diff_rorp, dsrp)
finalizer(index, dsrp)
SaveState.checkpoint(ITR, finalizer, dsrp) SaveState.checkpoint(ITR, finalizer, dsrp)
cls.check_skip_error(ITR.Finish, dsrp) ITR.Finish()
cls.check_skip_error(finalizer.Finish, dsrp) finalizer.Finish()
except: cls.handle_last_error(dsrp, finalizer, ITR) except: cls.handle_last_error(dsrp, finalizer, ITR)
if Globals.preserve_hardlinks: Hardlink.final_writedata() if Globals.preserve_hardlinks: Hardlink.final_writedata()
Stats.close_dir_stats_file() Stats.close_dir_stats_file()
Stats.write_session_statistics(ITR) Stats.write_session_statistics(ITR)
SaveState.checkpoint_remove() SaveState.checkpoint_remove()
def check_skip_error(cls, thunk, dsrp):
"""Run thunk, catch certain errors skip files"""
try: return thunk()
except (EnvironmentError, SkipFileException, DSRPPermError,
RPathException), exc:
if (not isinstance(exc, EnvironmentError) or
(errno.errorcode[exc[0]] in
['EPERM', 'ENOENT', 'EACCES', 'EBUSY', 'EEXIST',
'ENOTDIR', 'ENAMETOOLONG', 'EINTR', 'ENOTEMPTY',
'EIO', # reported by docv
'ETXTBSY' # reported by Campbell on some NT system
])):
Log.exception()
Log("Skipping file because of error after %s" %
(dsrp and dsrp.index,), 2)
return None
else:
Log.exception(1,2)
raise
def handle_last_error(cls, dsrp, finalizer, ITR): def handle_last_error(cls, dsrp, finalizer, ITR):
"""If catch fatal error, try to checkpoint before exiting""" """If catch fatal error, try to checkpoint before exiting"""
Log.exception(1) Log.exception(1, 2)
TracebackArchive.log()
SaveState.checkpoint(ITR, finalizer, dsrp, 1) SaveState.checkpoint(ITR, finalizer, dsrp, 1)
if Globals.preserve_hardlinks: Hardlink.final_checkpoint(Globals.rbdir) if Globals.preserve_hardlinks: Hardlink.final_checkpoint(Globals.rbdir)
SaveState.touch_last_file_definitive() SaveState.touch_last_file_definitive()
......
import traceback
execfile("statistics.py") execfile("statistics.py")
####################################################################### #######################################################################
...@@ -121,7 +122,7 @@ class Inc: ...@@ -121,7 +122,7 @@ class Inc:
MakeStatic(Inc) MakeStatic(Inc)
class IncrementITR(StatsITR): class IncrementITR(ErrorITR, StatsITR):
"""Patch and increment mirror directory """Patch and increment mirror directory
This has to be an ITR because directories that have files in them This has to be an ITR because directories that have files in them
...@@ -236,13 +237,8 @@ class IncrementITR(StatsITR): ...@@ -236,13 +237,8 @@ class IncrementITR(StatsITR):
def end_process(self): def end_process(self):
"""Do final work when leaving a tree (directory)""" """Do final work when leaving a tree (directory)"""
try: diff_rorp, dsrp, incpref = self.diff_rorp, self.dsrp, self.incpref diff_rorp, dsrp, incpref = self.diff_rorp, self.dsrp, self.incpref
except AttributeError: # This weren't set because of some error if self.mirror_isdirectory and (diff_rorp or self.changed):
return
if self.mirror_isdirectory:
if not diff_rorp and not self.changed: return
if self.directory_replacement: if self.directory_replacement:
tf = self.directory_replacement tf = self.directory_replacement
self.incrp = Robust.chain( self.incrp = Robust.chain(
...@@ -264,7 +260,7 @@ class IncrementITR(StatsITR): ...@@ -264,7 +260,7 @@ class IncrementITR(StatsITR):
self.add_file_stats(subinstance) self.add_file_stats(subinstance)
class MirrorITR(StatsITR): class MirrorITR(ErrorITR, StatsITR):
"""Like IncrementITR, but only patch mirror directory, don't increment""" """Like IncrementITR, but only patch mirror directory, don't increment"""
# This is always None since no increments will be created # This is always None since no increments will be created
incrp = None incrp = None
...@@ -284,13 +280,9 @@ class MirrorITR(StatsITR): ...@@ -284,13 +280,9 @@ class MirrorITR(StatsITR):
def end_process(self): def end_process(self):
"""Update statistics when leaving""" """Update statistics when leaving"""
try: diff_rorp, mirror_dsrp = self.diff_rorp, self.mirror_dsrp self.end_stats(self.diff_rorp, self.mirror_dsrp)
except AttributeError: # Some error above prevented these being set if self.mirror_dsrp.isdir():
return Stats.write_dir_stats_line(self, self.mirror_dsrp.index)
self.end_stats(diff_rorp, mirror_dsrp)
if mirror_dsrp.isdir():
Stats.write_dir_stats_line(self, mirror_dsrp.index)
def branch_process(self, subinstance): def branch_process(self, subinstance):
"""Update statistics with subdirectory results""" """Update statistics with subdirectory results"""
......
...@@ -196,9 +196,9 @@ class IterTreeReducer: ...@@ -196,9 +196,9 @@ class IterTreeReducer:
iterator nature of the connection between hosts and the temporal iterator nature of the connection between hosts and the temporal
order in which the files are processed. order in which the files are processed.
There are three stub functions below: start_process, end_process, There are four stub functions below: start_process, end_process,
and branch_process. A class that subclasses this one should fill branch_process, and check_for_errors. A class that subclasses
in these functions with real values. this one will probably fill in these functions to do more.
It is important that this class be pickable, so keep that in mind It is important that this class be pickable, so keep that in mind
when subclassing (this is used to resume failed sessions). when subclassing (this is used to resume failed sessions).
...@@ -210,6 +210,7 @@ class IterTreeReducer: ...@@ -210,6 +210,7 @@ class IterTreeReducer:
self.index = None self.index = None
self.subinstance = None self.subinstance = None
self.finished = None self.finished = None
self.caught_exception, self.start_successful = None, None
def intree(self, index): def intree(self, index):
"""Return true if index is still in current tree""" """Return true if index is still in current tree"""
...@@ -239,14 +240,33 @@ class IterTreeReducer: ...@@ -239,14 +240,33 @@ class IterTreeReducer:
"""Process a branch right after it is finished (stub)""" """Process a branch right after it is finished (stub)"""
pass pass
def check_for_errors(self, function, *args):
"""start/end_process is called by this function
Usually it will distinguish between two types of errors. Some
are serious and will be reraised, others are caught and simply
invalidate the current instance by setting
self.caught_exception.
"""
try: return apply(function, args)
except: raise
def Finish(self): def Finish(self):
"""Call at end of sequence to tie everything up""" """Call at end of sequence to tie everything up"""
assert not self.finished, (self.base_index, self.index) if not self.start_successful or self.finished:
if self.subinstance: self.caught_exception = 1
self.subinstance.Finish() if self.caught_exception: self.log_prev_error(self.index)
self.branch_process(self.subinstance) else:
self.end_process() if self.subinstance:
self.finished = 1 self.subinstance.Finish()
self.branch_process(self.subinstance)
self.check_for_errors(self.end_process)
self.finished = 1
def log_prev_error(self, index):
"""Call function if no pending exception"""
Log("Skipping %s because of previous error" % os.path.join(*index), 2)
def __call__(self, *args): def __call__(self, *args):
"""Process args, where args[0] is current position in iterator """Process args, where args[0] is current position in iterator
...@@ -263,18 +283,37 @@ class IterTreeReducer: ...@@ -263,18 +283,37 @@ class IterTreeReducer:
assert type(index) is types.TupleType, type(index) assert type(index) is types.TupleType, type(index)
if self.index is None: if self.index is None:
self.start_process(*args) self.check_for_errors(self.start_process, *args)
self.start_successful = 1
self.index = self.base_index = index self.index = self.base_index = index
return 1 return 1
if index <= self.index: if index <= self.index:
Log("Warning: oldindex %s >= newindex %s" % (self.index, index), 2) Log("Warning: oldindex %s >= newindex %s" % (self.index, index), 2)
return 1
if not self.intree(index): if not self.intree(index):
self.Finish() self.Finish()
return None return None
else:
self.process_w_subinstance(args) if self.caught_exception: self.log_prev_error(index)
self.index = index else: self.process_w_subinstance(args)
return 1 self.index = index
return 1
class ErrorITR(IterTreeReducer):
"""Adds some error handling to above ITR, if ITR processes files"""
def on_error(self, exc, *args):
"""This is run on any exception in start/end-process"""
self.caught_exception = 1
if args and isinstance(args[0], tuple):
filename = os.path.join(*args[0])
elif self.index: filename = os.path.join(*self.index)
else: filename = "."
Log("Error '%s' processing %s" % (exc, filename), 2)
def check_for_errors(self, function, *args):
"""Catch some non-fatal errors"""
return Robust.check_common_error(self.on_error, function, *args)
import time, sys import time, sys, traceback
execfile("lazy.py") execfile("lazy.py")
####################################################################### #######################################################################
...@@ -121,7 +121,13 @@ class Logger: ...@@ -121,7 +121,13 @@ class Logger:
Globals.Main.cleanup() Globals.Main.cleanup()
sys.exit(1) sys.exit(1)
def exception(self, only_terminal = 0, verbosity = 4): def exception_to_string(self):
"""Return string version of current exception"""
type, value, tb = sys.exc_info()
return ("Exception '%s' raised of class '%s':\n%s" %
(value, type, "".join(traceback.format_tb(tb))))
def exception(self, only_terminal = 0, verbosity = 5):
"""Log an exception and traceback """Log an exception and traceback
If only_terminal is None, log normally. If it is 1, then only If only_terminal is None, log normally. If it is 1, then only
...@@ -135,9 +141,6 @@ class Logger: ...@@ -135,9 +141,6 @@ class Logger:
logging_func = self.__call__ logging_func = self.__call__
else: logging_func = self.log_to_term else: logging_func = self.log_to_term
exc_info = sys.exc_info() logging_func(self.exception_to_string(), verbosity)
logging_func("Exception %s raised of class %s" %
(exc_info[1], exc_info[0]), verbosity)
logging_func("".join(traceback.format_tb(exc_info[2])), verbosity+1)
Log = Logger() Log = Logger()
...@@ -68,6 +68,7 @@ class RobustAction: ...@@ -68,6 +68,7 @@ class RobustAction:
return self.final_func(init_val) return self.final_func(init_val)
except Exception, exc: # Catch all errors except Exception, exc: # Catch all errors
Log.exception() Log.exception()
TracebackArchive.add()
if ran_init_thunk: self.error_handler(exc, 1, init_val) if ran_init_thunk: self.error_handler(exc, 1, init_val)
else: self.error_handler(exc, None, None) else: self.error_handler(exc, None, None)
raise exc raise exc
...@@ -233,41 +234,59 @@ class Robust: ...@@ -233,41 +234,59 @@ class Robust:
tf.setdata() tf.setdata()
return Robust.make_tf_robustaction(init, tf, rp) return Robust.make_tf_robustaction(init, tf, rp)
def check_common_error(init_thunk, error_thunk = lambda exc: None): def check_common_error(error_handler, function, *args):
"""Execute init_thunk, if error, run error_thunk on exception """Apply function to args, if error, run error_handler on exception
This only catches certain exceptions which seems innocent This only catches certain exceptions which seems innocent
enough. enough.
""" """
try: return init_thunk() try: return function(*args)
except (EnvironmentError, SkipFileException, DSRPPermError, except (EnvironmentError, SkipFileException, DSRPPermError,
RPathException, RdiffException), exc: RPathException, RdiffException), exc:
TracebackArchive.add()
if (not isinstance(exc, EnvironmentError) or if (not isinstance(exc, EnvironmentError) or
(errno.errorcode[exc[0]] in (errno.errorcode[exc[0]] in
['EPERM', 'ENOENT', 'EACCES', 'EBUSY', 'EEXIST', ['EPERM', 'ENOENT', 'EACCES', 'EBUSY', 'EEXIST',
'ENOTDIR', 'ENAMETOOLONG', 'EINTR', 'ENOTEMPTY', 'ENOTDIR', 'ENAMETOOLONG', 'EINTR', 'ENOTEMPTY',
'EIO', # reported by docv 'EIO', 'ETXTBSY', 'ESRCH', 'EINVAL'])):
'ETXTBSY' # reported by Campbell on some NT system
])):
Log.exception() Log.exception()
return error_thunk(exc) if error_handler: return error_handler(exc, *args)
else: else:
Log.exception(1, 2) Log.exception(1, 2)
raise raise
def listrp(rp): def listrp(rp):
"""Like rp.listdir() but return [] if error, and sort results""" """Like rp.listdir() but return [] if error, and sort results"""
def error_thunk(exc): def error_handler(exc):
Log("Error listing directory %s" % rp.path, 2) Log("Error listing directory %s" % rp.path, 2)
return [] return []
dir_listing = Robust.check_common_error(rp.listdir, error_thunk) dir_listing = Robust.check_common_error(error_handler, rp.listdir)
dir_listing.sort() dir_listing.sort()
return dir_listing return dir_listing
MakeStatic(Robust) MakeStatic(Robust)
class TracebackArchive:
"""Save last 10 caught exceptions, so they can be printed if fatal"""
_traceback_strings = []
def add(cls):
"""Add most recent exception to archived list"""
cls._traceback_strings.append(Log.exception_to_string())
if len(cls._traceback_strings) > 10:
cls._traceback_strings = cls._traceback_strings[:10]
def log(cls):
"""Print all exception information to log file"""
if cls._traceback_strings:
Log("------------ Old traceback info -----------\n%s"
"-------------------------------------------" %
("\n".join(cls._traceback_strings),), 3)
MakeClass(TracebackArchive)
class TempFileManager: class TempFileManager:
"""Manage temp files""" """Manage temp files"""
......
...@@ -57,6 +57,10 @@ class RORPIter: ...@@ -57,6 +57,10 @@ class RORPIter:
def Signatures(rp_iter): def Signatures(rp_iter):
"""Yield signatures of rpaths in given rp_iter""" """Yield signatures of rpaths in given rp_iter"""
def error_handler(exc, rp):
Log("Error generating signature for %s" % rp.path)
return None
for rp in rp_iter: for rp in rp_iter:
if rp.isplaceholder(): yield rp if rp.isplaceholder(): yield rp
else: else:
...@@ -65,11 +69,9 @@ class RORPIter: ...@@ -65,11 +69,9 @@ class RORPIter:
if rp.isflaglinked(): rorp.flaglinked() if rp.isflaglinked(): rorp.flaglinked()
else: else:
fp = Robust.check_common_error( fp = Robust.check_common_error(
lambda: Rdiff.get_signature(rp)) error_handler, Rdiff.get_signature, rp)
if fp: rorp.setfile(fp) if fp: rorp.setfile(fp)
else: else: continue
Log("Error generating signature for %s" % rp.path)
continue
yield rorp yield rorp
def GetSignatureIter(base_rp): def GetSignatureIter(base_rp):
......
...@@ -91,10 +91,10 @@ class Select: ...@@ -91,10 +91,10 @@ class Select:
self.starting_index = starting_index self.starting_index = starting_index
self.iter = self.iterate_starting_from(self.dsrpath, self.iter = self.iterate_starting_from(self.dsrpath,
self.iterate_starting_from, sel_func) self.iterate_starting_from, sel_func)
else: else: self.iter = self.Iterate(self.dsrpath, self.Iterate, sel_func)
assert not iterate_parents
self.iter = self.Iterate(self.dsrpath, self.Iterate, sel_func) # only iterate parents if we are not starting from beginning
self.iterate_parents = iterate_parents self.iterate_parents = starting_index is not None and iterate_parents
self.next = self.iter.next self.next = self.iter.next
self.__iter__ = lambda: self self.__iter__ = lambda: self
return self return self
...@@ -127,17 +127,18 @@ class Select: ...@@ -127,17 +127,18 @@ class Select:
def iterate_in_dir(self, dsrpath, rec_func, sel_func): def iterate_in_dir(self, dsrpath, rec_func, sel_func):
"""Iterate the dsrps in directory dsrpath.""" """Iterate the dsrps in directory dsrpath."""
def error_handler(exc, filename):
Log("Error initializing file %s/%s" % (dsrpath.path, filename), 2)
return None
if self.quoting_on: if self.quoting_on:
for subdir in FilenameMapping.get_quoted_dir_children(dsrpath): for subdir in FilenameMapping.get_quoted_dir_children(dsrpath):
for dsrp in rec_func(subdir, rec_func, sel_func): yield dsrp for dsrp in rec_func(subdir, rec_func, sel_func): yield dsrp
else: else:
for filename in Robust.listrp(dsrpath): for filename in Robust.listrp(dsrpath):
new_dsrp = Robust.check_common_error( new_dsrp = Robust.check_common_error(
lambda: dsrpath.append(filename)) error_handler, dsrpath.append, filename)
if not new_dsrp: if new_dsrp:
Log("Error initializing file %s/%s" %
(dsrpath.path, filename), 2)
else:
for dsrp in rec_func(new_dsrp, rec_func, sel_func): for dsrp in rec_func(new_dsrp, rec_func, sel_func):
yield dsrp yield dsrp
...@@ -208,7 +209,6 @@ class Select: ...@@ -208,7 +209,6 @@ class Select:
self.parse_last_excludes() self.parse_last_excludes()
self.parse_rbdir_exclude() self.parse_rbdir_exclude()
self.parse_proc_exclude()
def parse_catch_error(self, exc): def parse_catch_error(self, exc):
"""Deal with selection error exc""" """Deal with selection error exc"""
...@@ -231,11 +231,6 @@ pattern (such as '**') which matches the base directory.""" % ...@@ -231,11 +231,6 @@ pattern (such as '**') which matches the base directory.""" %
self.add_selection_func( self.add_selection_func(
self.glob_get_tuple_sf(("rdiff-backup-data",), 0), 1) self.glob_get_tuple_sf(("rdiff-backup-data",), 0), 1)
def parse_proc_exclude(self):
"""Exclude the /proc directory if starting from /"""
if self.prefix == "/":
self.add_selection_func(self.glob_get_tuple_sf(("proc",), 0), 1)
def parse_last_excludes(self): def parse_last_excludes(self):
"""Exit with error if last selection function isn't an exclude""" """Exit with error if last selection function isn't an exclude"""
if (self.selection_functions and if (self.selection_functions and
......
...@@ -286,7 +286,8 @@ class Stats: ...@@ -286,7 +286,8 @@ class Stats:
"directory_statistics"), Time.curtime, suffix) "directory_statistics"), Time.curtime, suffix)
if cls._dir_stats_rp.lstat(): if cls._dir_stats_rp.lstat():
Log("Warning, statistics file %s already exists, appending", 2) Log("Warning, statistics file %s already exists, appending" %
cls._dir_stats_rp.path, 2)
cls._dir_stats_fp = cls._dir_stats_rp.open("ab", cls._dir_stats_fp = cls._dir_stats_rp.open("ab",
Globals.compression) Globals.compression)
else: cls._dir_stats_fp = \ else: cls._dir_stats_fp = \
......
...@@ -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 -v5 --remote-schema './chdir-wrapper %s' " "/rdiff-backup -v3 --remote-schema './chdir-wrapper %s' "
def refresh(self, *rp_list): def refresh(self, *rp_list):
"""Reread data for the given rps""" """Reread data for the given rps"""
......
...@@ -6,7 +6,7 @@ rbexec("main.py") ...@@ -6,7 +6,7 @@ rbexec("main.py")
lc = Globals.local_connection lc = Globals.local_connection
Globals.change_source_perms = 1 Globals.change_source_perms = 1
Log.setverbosity(5) Log.setverbosity(3)
def getrp(ending): def getrp(ending):
return RPath(lc, "testfiles/various_file_types/" + ending) return RPath(lc, "testfiles/various_file_types/" + ending)
......
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