Commit dc0f49a4 authored by bescoto's avatar bescoto

Fixed various --restrict options, some refactoring


git-svn-id: http://svn.savannah.nongnu.org/svn/rdiff-backup@407 2b77aa54-bcbc-44c9-a7ec-4f6cf2b41109
parent 5e19836b
......@@ -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)
---------------------------
......
......@@ -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:
......
......@@ -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",
......
......@@ -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
......
......@@ -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
......@@ -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()
......
......@@ -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()
......
......@@ -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()
......
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