summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbescoto <bescoto@2b77aa54-bcbc-44c9-a7ec-4f6cf2b41109>2003-08-27 20:18:22 +0000
committerbescoto <bescoto@2b77aa54-bcbc-44c9-a7ec-4f6cf2b41109>2003-08-27 20:18:22 +0000
commit9e9ac86ba4d50979975b624ce3948a1011af3fd1 (patch)
tree7e7b233f9715ac00ab78ff72d0df99fcd3b55d65
parenta1a2ba7783c700cd73b640cac7388fa6c733bf8b (diff)
downloadrdiff-backup-9e9ac86ba4d50979975b624ce3948a1011af3fd1.tar.gz
Fixed various --restrict options, some refactoring
git-svn-id: http://svn.savannah.nongnu.org/svn/rdiff-backup/trunk@407 2b77aa54-bcbc-44c9-a7ec-4f6cf2b41109
-rw-r--r--rdiff-backup/CHANGELOG3
-rw-r--r--rdiff-backup/rdiff_backup/Main.py76
-rw-r--r--rdiff-backup/rdiff_backup/Security.py42
-rw-r--r--rdiff-backup/rdiff_backup/backup.py3
-rw-r--r--rdiff-backup/rdiff_backup/fs_abilities.py134
-rw-r--r--rdiff-backup/rdiff_backup/rpath.py6
-rw-r--r--rdiff-backup/testing/finaltest.py12
-rw-r--r--rdiff-backup/testing/regressiontest.py3
8 files changed, 168 insertions, 111 deletions
diff --git a/rdiff-backup/CHANGELOG b/rdiff-backup/CHANGELOG
index 11d316e..a123b87 100644
--- a/rdiff-backup/CHANGELOG
+++ b/rdiff-backup/CHANGELOG
@@ -16,6 +16,9 @@ Fixed bug in EA/ACL restoring, noticed by Greg Freemyer. Also updated
quoting of filenames and extended attributes names to match
forthcoming attr/facl utilities.
+Fixed problems with --restrict options that would cause proper
+sessions to fail. Thanks to Randall Nortman for error report.
+
New in v0.13.1 (2003/08/08)
---------------------------
diff --git a/rdiff-backup/rdiff_backup/Main.py b/rdiff-backup/rdiff_backup/Main.py
index b476124..ab26903 100644
--- a/rdiff-backup/rdiff_backup/Main.py
+++ b/rdiff-backup/rdiff_backup/Main.py
@@ -230,14 +230,13 @@ def Main(arglist):
def Backup(rpin, rpout):
"""Backup, possibly incrementally, src_path to dest_path."""
+ global incdir
SetConnections.BackupInitConnections(rpin.conn, rpout.conn)
backup_check_dirs(rpin, rpout)
- backup_set_fs_globals(rpin, rpout)
- if Globals.chars_to_quote:
- rpout = FilenameMapping.get_quotedrpath(rpout)
- SetConnections.UpdateGlobal(
- 'rbdir', FilenameMapping.get_quotedrpath(Globals.rbdir))
backup_set_rbdir(rpin, rpout)
+ backup_set_fs_globals(rpin, rpout)
+ if Globals.chars_to_quote: rpout = backup_quoted_rpaths(rpout)
+ backup_final_init(rpout)
backup_set_select(rpin)
if prevtime:
rpout.conn.Main.backup_touch_curmirror_local(rpin, rpout)
@@ -248,6 +247,14 @@ def Backup(rpin, rpout):
backup.Mirror(rpin, rpout)
rpout.conn.Main.backup_touch_curmirror_local(rpin, rpout)
+def backup_quoted_rpaths(rpout):
+ """Get QuotedRPath versions of important RPaths. Return rpout"""
+ global incdir
+ SetConnections.UpdateGlobal(
+ 'rbdir', FilenameMapping.get_quotedrpath(Globals.rbdir))
+ incdir = FilenameMapping.get_quotedrpath(incdir)
+ return FilenameMapping.get_quotedrpath(rpout)
+
def backup_set_select(rpin):
"""Create Select objects on source connection"""
rpin.conn.backup.SourceStruct.set_source_select(rpin, select_opts,
@@ -275,11 +282,9 @@ def backup_check_dirs(rpin, rpout):
def backup_set_rbdir(rpin, rpout):
"""Initialize data dir and logging"""
- global incdir, prevtime
+ global incdir
SetConnections.UpdateGlobal('rbdir', Globals.rbdir)
- checkdest_if_necessary(rpout)
incdir = Globals.rbdir.append_path("increments")
- prevtime = backup_get_mirrortime()
assert rpout.lstat(), (rpout.path, rpout.lstat())
if rpout.isdir() and not rpout.listdir(): # rpout is empty dir
@@ -296,11 +301,6 @@ want to update or overwrite it, run rdiff-backup with the --force
option.""" % rpout.path)
if not Globals.rbdir.lstat(): Globals.rbdir.mkdir()
- inc_base = Globals.rbdir.append_path("increments")
- if not inc_base.lstat(): inc_base.mkdir()
- if Log.verbosity > 0:
- Log.open_logfile(Globals.rbdir.append("backup.log"))
- ErrorLog.open(Time.curtimestr, compress = Globals.compression)
def backup_warn_if_infinite_regress(rpin, rpout):
"""Warn user if destination area contained in source area"""
@@ -325,6 +325,21 @@ def backup_get_mirrortime():
if mirror_rps: return mirror_rps[0].getinctime()
else: return None
+def backup_final_init(rpout):
+ """Open the backup log and the error log, create increments dir"""
+ global prevtime
+ prevtime = backup_get_mirrortime()
+ need_check = checkdest_need_check(rpout)
+ if Log.verbosity > 0:
+ Log.open_logfile(Globals.rbdir.append("backup.log"))
+ ErrorLog.open(Time.curtimestr, compress = Globals.compression)
+ if need_check:
+ Log("Previous backup seems to have failed, regressing "
+ "destination now.", 2)
+ rpout.conn.regress.Regress(rpout)
+ inc_base = Globals.rbdir.append_path("increments")
+ if not inc_base.lstat(): inc_base.mkdir()
+
def backup_set_fs_globals(rpin, rpout):
"""Use fs_abilities to set the globals that depend on filesystem"""
def update_bool_global(attr, bool):
@@ -332,10 +347,10 @@ def backup_set_fs_globals(rpin, rpout):
if Globals.get(attr) is None:
SetConnections.UpdateGlobal(attr, bool)
- src_fsa = fs_abilities.FSAbilities('source').init_readonly(rpin)
+ src_fsa = rpin.conn.fs_abilities.get_fsabilities_readonly('source', rpin)
Log(str(src_fsa), 3)
- dest_fsa = fs_abilities.FSAbilities('destination').init_readwrite(
- Globals.rbdir, override_chars_to_quote = Globals.chars_to_quote)
+ dest_fsa = rpout.conn.fs_abilities.get_fsabilities_readwrite(
+ 'destination', Globals.rbdir, 1, Globals.chars_to_quote)
Log(str(dest_fsa), 3)
update_bool_global('read_acls', src_fsa.acls)
@@ -425,10 +440,10 @@ def restore_set_fs_globals(target):
"""If bool is not None, update Globals.attr accordingly"""
if Globals.get(attr) is None: SetConnections.UpdateGlobal(attr, bool)
- target_fsa = fs_abilities.FSAbilities('destination').init_readwrite(
- target, 0)
+ target_fsa = target.conn.fs_abilities.get_fsabilities_readwrite(
+ 'destination', target, 0)
Log(str(target_fsa), 3)
- mirror_fsa = fs_abilities.FSAbilities('source').init_readwrite(
+ mirror_fsa = Globals.rbdir.conn.fs_abilities.get_fsabilities_restoresource(
Globals.rbdir)
Log(str(mirror_fsa), 3)
@@ -462,7 +477,8 @@ def restore_set_select(mirror_rp, target):
def restore_start_log(rpin, target, time):
"""Open restore log file, log initial message"""
try: Log.open_logfile(Globals.rbdir.append("restore.log"))
- except LoggerError, e: Log("Warning, " + str(e), 2)
+ except (LoggerError, Security.Violation), e:
+ Log("Warning - Unable to open logfile: " + str(e), 2)
# Log following message at file verbosity 3, but term verbosity 4
log_message = ("Starting restore of %s to %s as it was as of %s." %
@@ -513,11 +529,15 @@ def restore_set_root(rpin):
global restore_root, restore_index, restore_root_set
if rpin.isincfile(): relpath = rpin.getincbase().path
else: relpath = rpin.path
- pathcomps = os.path.join(rpin.conn.os.getcwd(), relpath).split("/")
- assert len(pathcomps) >= 2 # path should be relative to /
+ if rpin.conn is not Globals.local_connection:
+ # For security checking consistency, don't get absolute path
+ pathcomps = relpath.split('/')
+ else: pathcomps = os.path.join(os.getcwd(), relpath).split("/")
+ if not pathcomps[0]: min_len_pathcomps = 2 # treat abs paths differently
+ else: min_len_pathcomps = 1
i = len(pathcomps)
- while i >= 2:
+ while i >= min_len_pathcomps:
parent_dir = rpath.RPath(rpin.conn, "/".join(pathcomps[:i]))
if (parent_dir.isdir() and
"rdiff-backup-data" in parent_dir.listdir()): break
@@ -662,8 +682,14 @@ def checkdest_need_check(dest_rp):
The rdiff-backup data directory
%s
exists, but we cannot find a valid current_mirror marker. You can
-avoid this message by removing this directory; however any data in it
-will be lost.
+avoid this message by removing the rdiff_backup_data directory;
+however any data in it will be lost.
+
+Probably this error was caused because the first rdiff-backup session
+into a new directory failed. If this is the case it is safe to delete
+the rdiff_backup_data directory because there is no important
+information in it.
+
""" % (Globals.rbdir.path,))
elif len(curmir_incs) == 1: return 0
else:
diff --git a/rdiff-backup/rdiff_backup/Security.py b/rdiff-backup/rdiff_backup/Security.py
index 919261a..6ee6aa6 100644
--- a/rdiff-backup/rdiff_backup/Security.py
+++ b/rdiff-backup/rdiff_backup/Security.py
@@ -117,35 +117,45 @@ def set_allowed_requests(sec_level):
"Log.log_to_file",
"SetConnections.add_redirected_conn",
"RedirectedRun",
- "sys.stdout.write"]
+ "sys.stdout.write",
+ "robust.install_signal_handlers"]
if sec_level == "minimal": pass
elif sec_level == "read-only" or sec_level == "update-only":
allowed_requests.extend(
["C.make_file_dict",
+ "rpath.ea_get",
+ "rpath.acl_get",
"log.Log.log_to_file",
"os.getuid",
"os.listdir",
"Time.setcurtime_local",
- "robust.Resume.ResumeCheck",
- "backup.SourceStruct.split_initial_dsiter",
- "backup.SourceStruct.get_diffs_and_finalize",
"rpath.gzip_open_local_read",
- "rpath.open_local_read"])
- if sec_level == "update-only":
+ "rpath.open_local_read",
+ "Hardlink.initialize_dictionaries"])
+ if sec_level == "read-only":
allowed_requests.extend(
- ["Log.open_logfile_local", "Log.close_logfile_local",
- "Log.close_logfile_allconn", "Log.log_to_file",
- "log.Log.log_to_file",
- "robust.SaveState.init_filenames",
- "robust.SaveState.touch_last_file",
- "backup.DestinationStruct.get_sigs",
- "backup.DestinationStruct.patch_w_datadir_writes",
- "backup.DestinationStruct.patch_and_finalize",
- "backup.DestinationStruct.patch_increment_and_finalize",
+ ["fs_abilities.get_fsabilities_readonly",
+ "fs_abilities.get_fsabilities_restoresource",
+ "restore.MirrorStruct.set_mirror_and_rest_times",
+ "restore.MirrorStruct.initialize_rf_cache",
+ "restore.MirrorStruct.get_diffs",
+ "backup.SourceStruct.get_source_select",
+ "backup.SourceStruct.set_source_select",
+ "backup.SourceStruct.get_diffs"])
+ elif sec_level == "update-only":
+ allowed_requests.extend(
+ ["log.Log.open_logfile_local", "log.Log.close_logfile_local",
+ "log.ErrorLog.open", "log.ErrorLog.isopen",
+ "log.ErrorLog.close",
+ "backup.DestinationStruct.set_rorp_cache",
+ "backup.DestinationStruct.get_sigs",
+ "backup.DestinationStruct.patch_and_increment",
"Main.backup_touch_curmirror_local",
+ "Main.backup_remove_curmirror_local",
"Globals.ITRB.increment_stat",
"statistics.record_error",
- "log.ErrorLog.write_if_open"])
+ "log.ErrorLog.write_if_open",
+ "fs_abilities.get_fsabilities_readwrite"])
if Globals.server:
allowed_requests.extend(
["SetConnections.init_connection_remote",
diff --git a/rdiff-backup/rdiff_backup/backup.py b/rdiff-backup/rdiff_backup/backup.py
index 0909722..0c66ca0 100644
--- a/rdiff-backup/rdiff_backup/backup.py
+++ b/rdiff-backup/rdiff_backup/backup.py
@@ -27,6 +27,7 @@ import Globals, metadata, rorpiter, TempFile, Hardlink, robust, increment, \
def Mirror(src_rpath, dest_rpath):
"""Turn dest_rpath into a copy of src_rpath"""
+ log.Log("Starting mirror %s to %s" % (src_rpath.path, dest_rpath.path), 4)
SourceS = src_rpath.conn.backup.SourceStruct
DestS = dest_rpath.conn.backup.DestinationStruct
@@ -38,6 +39,8 @@ def Mirror(src_rpath, dest_rpath):
def Mirror_and_increment(src_rpath, dest_rpath, inc_rpath):
"""Mirror + put increments in tree based at inc_rpath"""
+ log.Log("Starting increment operation %s to %s" %
+ (src_rpath.path, dest_rpath.path), 4)
SourceS = src_rpath.conn.backup.SourceStruct
DestS = dest_rpath.conn.backup.DestinationStruct
diff --git a/rdiff-backup/rdiff_backup/fs_abilities.py b/rdiff-backup/rdiff_backup/fs_abilities.py
index 36603ef..20b85fd 100644
--- a/rdiff-backup/rdiff_backup/fs_abilities.py
+++ b/rdiff-backup/rdiff_backup/fs_abilities.py
@@ -97,7 +97,7 @@ class FSAbilities:
return '\n'.join(s)
def init_readonly(self, rp):
- """Set variables using fs tested at RPath rp
+ """Set variables using fs tested at RPath rp. Run locally.
This method does not write to the file system at all, and
should be run on the file system when the file system will
@@ -106,6 +106,7 @@ class FSAbilities:
Only self.acls and self.eas are set.
"""
+ assert rp.conn is Globals.local_connection
self.root_rp = rp
self.read_only = 1
self.set_eas(rp, 0)
@@ -115,7 +116,7 @@ class FSAbilities:
def init_readwrite(self, rbdir, use_ctq_file = 1,
override_chars_to_quote = None):
- """Set variables using fs tested at rp_base
+ """Set variables using fs tested at rp_base. Run locally.
This method creates a temp directory in rp_base and writes to
it in order to test various features. Use on a file system
@@ -128,13 +129,14 @@ class FSAbilities:
file in directory.
"""
+ assert rbdir.conn is Globals.local_connection
if not rbdir.isdir():
assert not rbdir.lstat(), (rbdir.path, rbdir.lstat())
rbdir.mkdir()
self.root_rp = rbdir
self.read_only = 0
- subdir = rbdir.conn.TempFile.new_in_dir(rbdir)
+ subdir = TempFile.new_in_dir(rbdir)
subdir.mkdir()
self.set_ownership(subdir)
self.set_hardlinks(subdir)
@@ -211,7 +213,13 @@ rdiff-backup-data/chars_to_quote.
def set_fsync_dirs(self, testdir):
"""Set self.fsync_dirs if directories can be fsync'd"""
- self.fsync_dirs = testdir.conn.fs_abilities.test_fsync_local(testdir)
+ assert testdir.conn is Globals.local_connection
+ try: testdir.fsync()
+ except (IOError, OSError), exc:
+ log.Log("Directories on file system at %s are not fsyncable.\n"
+ "Assuming it's unnecessary." % (testdir.path,), 4)
+ self.fsync_dirs = 0
+ else: self.fsync_dirs = 1
def set_chars_to_quote(self, subdir):
"""Set self.chars_to_quote by trying to write various paths"""
@@ -259,11 +267,48 @@ rdiff-backup-data/chars_to_quote.
def set_acls(self, rp):
"""Set self.acls based on rp. Does not write. Needs to be local"""
- self.acls = rp.conn.fs_abilities.test_acls_local(rp)
+ assert Globals.local_connection is rp.conn
+ assert rp.lstat()
+ try: import posix1e
+ except ImportError:
+ log.Log("Unable to import module posix1e from pylibacl "
+ "package.\nACLs not supported on filesystem at %s" %
+ (rp.path,), 4)
+ self.acls = 0
+ return
+
+ try: posix1e.ACL(file=rp.path)
+ except IOError, exc:
+ if exc[0] == errno.EOPNOTSUPP:
+ log.Log("ACLs appear not to be supported by "
+ "filesystem at %s" % (rp.path,), 4)
+ self.acls = 0
+ else: raise
+ else: self.acls = 1
def set_eas(self, rp, write):
"""Set extended attributes from rp. Tests writing if write is true."""
- self.eas = rp.conn.fs_abilities.test_eas_local(rp, write)
+ assert Globals.local_connection is rp.conn
+ assert rp.lstat()
+ try: import xattr
+ except ImportError:
+ log.Log("Unable to import module xattr. EAs not "
+ "supported on filesystem at %s" % (rp.path,), 4)
+ self.eas = 0
+ return
+
+ try:
+ xattr.listxattr(rp.path)
+ if write:
+ xattr.setxattr(rp.path, "user.test", "test val")
+ assert xattr.getxattr(rp.path, "user.test") == "test val"
+ except IOError, exc:
+ if exc[0] == errno.EOPNOTSUPP:
+ log.Log("Extended attributes not supported by "
+ "filesystem at %s" % (rp.path,), 4)
+ self.eas = 0
+ else: raise
+ else: self.eas = 1
def set_dir_inc_perms(self, rp):
"""See if increments can have full permissions like a directory"""
@@ -282,17 +327,17 @@ rdiff-backup-data/chars_to_quote.
def set_resource_fork_readwrite(self, dir_rp):
"""Test for resource forks by writing to regular_file/rsrc"""
+ assert dir_rp.conn is Globals.local_connection
reg_rp = dir_rp.append('regfile')
reg_rp.touch()
s = 'test string---this should end up in resource fork'
try:
- fp_write = reg_rp.conn.open(os.path.join(reg_rp.path, 'rsrc'),
- 'wb')
+ fp_write = open(os.path.join(reg_rp.path, 'rsrc'), 'wb')
fp_write.write(s)
assert not fp_write.close()
- fp_read = reg_rp.conn.open(os.path.join(reg_rp.path, 'rsrc'), 'rb')
+ fp_read = open(os.path.join(reg_rp.path, 'rsrc'), 'rb')
s_back = fp_read.read()
assert not fp_read.close()
except (OSError, IOError), e: self.resource_forks = 0
@@ -322,56 +367,23 @@ rdiff-backup-data/chars_to_quote.
self.resource_forks = 0
-def test_eas_local(rp, write):
- """Test ea support. Must be called locally. Usedy by set_eas above."""
- assert Globals.local_connection is rp.conn
- assert rp.lstat()
- try: import xattr
- except ImportError:
- log.Log("Unable to import module xattr. EAs not "
- "supported on filesystem at %s" % (rp.path,), 4)
- return 0
-
- try:
- xattr.listxattr(rp.path)
- if write:
- xattr.setxattr(rp.path, "user.test", "test val")
- assert xattr.getxattr(rp.path, "user.test") == "test val"
- except IOError, exc:
- if exc[0] == errno.EOPNOTSUPP:
- log.Log("Extended attributes not supported by "
- "filesystem at %s" % (rp.path,), 4)
- return 0
- else: raise
- else: return 1
-
-def test_acls_local(rp):
- """Test acl support. Call locally. Does not write."""
- assert Globals.local_connection is rp.conn
- assert rp.lstat()
- try: import posix1e
- except ImportError:
- log.Log("Unable to import module posix1e from pylibacl "
- "package.\nACLs not supported on filesystem at %s" %
- (rp.path,), 4)
- return 0
-
- try: posix1e.ACL(file=rp.path)
- except IOError, exc:
- if exc[0] == errno.EOPNOTSUPP:
- log.Log("ACLs appear not to be supported by "
- "filesystem at %s" % (rp.path,), 4)
- return 0
- else: raise
- else: return 1
-
-def test_fsync_local(rp):
- """Test fsyncing directories locally"""
- assert rp.conn is Globals.local_connection
- try: rp.fsync()
- except (IOError, OSError), exc:
- log.Log("Directories on file system at %s are not "
- "fsyncable.\nAssuming it's unnecessary." % (rp.path,), 4)
- return 0
- else: return 1
+def get_fsabilities_readonly(desc_string, rp):
+ """Return an FSAbilities object with given description_string
+
+ Will be initialized read_only with given RPath rp.
+
+ """
+ return FSAbilities(desc_string).init_readonly(rp)
+
+def get_fsabilities_readwrite(desc_string, rb, use_ctq_file = 1, ctq = None):
+ """Like above but initialize read/write and pass other arguments"""
+ return FSAbilities(desc_string).init_readwrite(
+ rb, use_ctq_file = use_ctq_file, override_chars_to_quote = ctq)
+def get_fsabilities_restoresource(rp):
+ """Used when restoring, get abilities of source directory"""
+ fsa = FSAbilities('source').init_readonly(rp)
+ ctq_rp = rp.append("chars_to_quote")
+ if ctq_rp.lstat(): fsa.chars_to_quote = ctq_rp.get_data()
+ else: fsa.chars_to_quote = ""
+ return fsa
diff --git a/rdiff-backup/rdiff_backup/rpath.py b/rdiff-backup/rdiff_backup/rpath.py
index c1641a5..a273f39 100644
--- a/rdiff-backup/rdiff_backup/rpath.py
+++ b/rdiff-backup/rdiff_backup/rpath.py
@@ -623,8 +623,10 @@ class RPath(RORPath):
def setdata(self):
"""Set data dictionary using C extension"""
self.data = self.conn.C.make_file_dict(self.path)
- if Globals.read_eas and self.lstat(): self.get_ea()
- if Globals.read_acls and self.lstat(): self.get_acl()
+ if Globals.read_eas and self.lstat():
+ self.data['ea'] = self.conn.rpath.ea_get(self)
+ if Globals.read_acls and self.lstat():
+ self.data['acl'] = self.conn.rpath.acl_get(self)
if Globals.read_resource_forks and self.isreg():
self.get_resource_fork()
diff --git a/rdiff-backup/testing/finaltest.py b/rdiff-backup/testing/finaltest.py
index 4f1caf9..85f3378 100644
--- a/rdiff-backup/testing/finaltest.py
+++ b/rdiff-backup/testing/finaltest.py
@@ -272,6 +272,12 @@ class Final(PathSetter):
# Back up increment3
self.exec_rb(30000, 'testfiles/win-increment3', 'testfiles/output')
+ # Now check to make sure no ":" in output directory
+ popen_fp = os.popen("find testfiles/output -name '*:*' | wc")
+ wc_output = popen_fp.read()
+ popen_fp.close()
+ assert wc_output.split() == ["0", "0", "0"], wc_output
+
# Start restore of increment 2
Globals.chars_to_quote = '^a-z0-9_ -.'
inc_paths = self.getinc_paths("increments.",
@@ -289,12 +295,6 @@ class Final(PathSetter):
compare_hardlinks = 0)
self.rb_schema = old_schema
- # Now check to make sure no ":" in output directory
- popen_fp = os.popen("find testfiles/output -name '*:*' | wc")
- wc_output = popen_fp.read()
- popen_fp.close()
- assert wc_output.split() == ["0", "0", "0"], wc_output
-
def testLegacy(self):
"""Test restoring directory with no mirror_metadata file"""
self.delete_tmpdirs()
diff --git a/rdiff-backup/testing/regressiontest.py b/rdiff-backup/testing/regressiontest.py
index c135387..a7db21c 100644
--- a/rdiff-backup/testing/regressiontest.py
+++ b/rdiff-backup/testing/regressiontest.py
@@ -386,8 +386,9 @@ class MirrorTest(PathSetter):
assert not rpout.append("rdiff-backup-data").lstat()
Main.misc_setup([rpin, rpout])
Main.backup_check_dirs(rpin, rpout)
- Main.backup_set_fs_globals(rpin, rpout)
Main.backup_set_rbdir(rpin, rpout)
+ Main.backup_set_fs_globals(rpin, rpout)
+ Main.backup_final_init(rpout)
Main.backup_set_select(rpin)
backup.Mirror(rpin, rpout)
log.ErrorLog.close()