Commit c5b4d0cd authored by owsla's avatar owsla

Revert previous patch & hold it for the new development branch


git-svn-id: http://svn.savannah.nongnu.org/svn/rdiff-backup@992 2b77aa54-bcbc-44c9-a7ec-4f6cf2b41109
parent e73f5576
New in v1.2.5 (????/??/??) New in v1.2.5 (????/??/??)
--------------------------- ---------------------------
Fix escape DOS devices and escape trailing spaces/periods. (Andrew Ferguson)
New in v1.2.4 (2009/01/01) New in v1.2.4 (2009/01/01)
......
...@@ -85,17 +85,18 @@ def quote(path): ...@@ -85,17 +85,18 @@ def quote(path):
""" """
QuotedPath = chars_to_quote_regexp.sub(quote_single, path) QuotedPath = chars_to_quote_regexp.sub(quote_single, path)
if not Globals.escape_dos_devices and not Globals.escape_trailing_spaces: if not Globals.must_escape_dos_devices \
and not Globals.must_escape_trailing_spaces:
return QuotedPath return QuotedPath
# Escape a trailing space or period (invalid in names on FAT32 under DOS, # Escape a trailing space or period (invalid in names on FAT32 under DOS,
# Windows and modern Linux) # Windows and modern Linux)
if Globals.escape_trailing_spaces: if Globals.must_escape_trailing_spaces:
if len(QuotedPath) and (QuotedPath[-1] == ' ' or QuotedPath[-1] == '.'): if len(QuotedPath) and (QuotedPath[-1] == ' ' or QuotedPath[-1] == '.'):
QuotedPath = QuotedPath[:-1] + \ QuotedPath = QuotedPath[:-1] + \
"%s%03d" % (quoting_char, ord(QuotedPath[-1])) "%s%03d" % (quoting_char, ord(QuotedPath[-1]))
if not Globals.escape_dos_devices: if not Globals.must_escape_dos_devices:
return QuotedPath return QuotedPath
# Escape first char of any special DOS device files even if filename has an # Escape first char of any special DOS device files even if filename has an
......
...@@ -860,8 +860,7 @@ def checkdest_need_check(dest_rp): ...@@ -860,8 +860,7 @@ def checkdest_need_check(dest_rp):
"""Return None if no dest dir found, 1 if dest dir needs check, 0 o/w""" """Return None if no dest dir found, 1 if dest dir needs check, 0 o/w"""
if not dest_rp.isdir() or not Globals.rbdir.isdir(): return None if not dest_rp.isdir() or not Globals.rbdir.isdir(): return None
for filename in Globals.rbdir.listdir(): for filename in Globals.rbdir.listdir():
if filename not in ['chars_to_quote', 'special_escapes', if filename not in ['chars_to_quote', 'backup.log']: break
'backup.log']: break
else: # This may happen the first backup just after we test for quoting else: # This may happen the first backup just after we test for quoting
return None return None
curmirroot = Globals.rbdir.append("current_mirror") curmirroot = Globals.rbdir.append("current_mirror")
......
...@@ -50,8 +50,8 @@ class FSAbilities: ...@@ -50,8 +50,8 @@ class FSAbilities:
high_perms = None # True if suid etc perms are (read/write) supported high_perms = None # True if suid etc perms are (read/write) supported
escape_dos_devices = None # True if dos device files can't be created (e.g., escape_dos_devices = None # True if dos device files can't be created (e.g.,
# aux, con, com1, etc) # aux, con, com1, etc)
escape_trailing_spaces = None # True if trailing spaces or periods at the escape_trailing_spaces = None # True if files with trailing spaces or
# end of filenames aren't preserved # periods can't be created
symlink_perms = None # True if symlink perms are affected by umask symlink_perms = None # True if symlink perms are affected by umask
def __init__(self, name = None): def __init__(self, name = None):
...@@ -131,7 +131,7 @@ class FSAbilities: ...@@ -131,7 +131,7 @@ class FSAbilities:
self.set_carbonfile() self.set_carbonfile()
self.set_case_sensitive_readonly(rp) self.set_case_sensitive_readonly(rp)
self.set_escape_dos_devices(rp) self.set_escape_dos_devices(rp)
self.set_escape_trailing_spaces_readonly(rp) self.set_escape_trailing_spaces(rp)
return self return self
def init_readwrite(self, rbdir): def init_readwrite(self, rbdir):
...@@ -166,7 +166,7 @@ class FSAbilities: ...@@ -166,7 +166,7 @@ class FSAbilities:
self.set_high_perms_readwrite(subdir) self.set_high_perms_readwrite(subdir)
self.set_symlink_perms(subdir) self.set_symlink_perms(subdir)
self.set_escape_dos_devices(subdir) self.set_escape_dos_devices(subdir)
self.set_escape_trailing_spaces_readwrite(subdir) self.set_escape_trailing_spaces(subdir)
subdir.delete() subdir.delete()
return self return self
...@@ -554,88 +554,44 @@ class FSAbilities: ...@@ -554,88 +554,44 @@ class FSAbilities:
sym_source.delete() sym_source.delete()
def set_escape_dos_devices(self, subdir): def set_escape_dos_devices(self, subdir):
"""Test if DOS device files can be used as filenames. """If special file con can be stat'd, escape special files"""
This test must detect if the underlying OS is Windows, whehter we are
running under Cygwin or natively. Cygwin allows these special files to
be stat'd from any directory. Native Windows returns OSError (like
non-Cygwin POSIX), but we can check for that using os.name.
Note that 'con' and 'aux' have some unusual behaviors as shown below.
os.lstat() | con aux prn
-------------+-------------------------------------
Unix | OSError,2 OSError,2 OSError,2
Cygwin/NTFS | -success- -success- -success-
Cygwin/FAT32 | -success- -HANGS-
Native Win | WinError,2 WinError,87 WinError,87
"""
if os.name == "nt":
self.escape_dos_devices = 1
return
try: try:
device_rp = subdir.append("con") device_rp = subdir.append("con")
if device_rp.lstat(): if device_rp.lstat():
log.Log("escape_dos_devices required by filesystem at %s" \
% (subdir.path), 4)
self.escape_dos_devices = 1 self.escape_dos_devices = 1
else: else:
self.escape_dos_devices = 0 log.Log("escape_dos_devices not required by filesystem at %s" \
% (subdir.path), 4)
self.escape_dos_devices = 0
except(OSError): except(OSError):
log.Log("escape_dos_devices required by filesystem at %s" \
% (subdir.path), 4)
self.escape_dos_devices = 1 self.escape_dos_devices = 1
def set_escape_trailing_spaces_readwrite(self, testdir): def set_escape_trailing_spaces(self, subdir):
"""Windows and Linux/FAT32 will not preserve trailing spaces or periods. # Disable this for 1.2.4
self.escape_trailing_spaces = 0
Linux/FAT32 behaves inconsistently: It will give an OSError,22 if return
os.mkdir() is called on a directory name with a space at the end, but
will give an IOError("invalid mode") if you attempt to create a filename
with a space at the end. However, if a period is placed at the end of
the name, Linux/FAT32 is consistent with Cygwin and Native Windows.
"""
period_rp = testdir.append("foo.")
assert not period_rp.lstat()
tmp_rp = testdir.append("foo") """If file with trailing space can't be created, escape such files"""
tmp_rp.touch() try:
assert tmp_rp.lstat() space_rp = subdir.append("test ")
space_rp.touch()
period_rp.setdata() if space_rp.lstat():
if period_rp.lstat(): log.Log("escape_trailing_spaces not required by filesystem " \
"at %s" % (subdir.path), 4)
self.escape_trailing_spaces = 0
space_rp.delete()
else:
log.Log("escape_trailing_spaces required by filesystem at %s" \
% (subdir.path), 4)
self.escape_trailing_spaces = 1
except (OSError, IOError):
log.Log("escape_trailing_spaces required by filesystem at %s" \
% (subdir.path), 4)
self.escape_trailing_spaces = 1 self.escape_trailing_spaces = 1
else:
self.escape_trailing_spaces = 0
tmp_rp.delete()
def set_escape_trailing_spaces_readonly(self, rp):
"""Determine if directory at rp permits filenames with trailing
spaces or periods without writing."""
def test_period(dir_rp, dirlist):
"""Return 1 if trailing spaces and periods should be escaped"""
filename = dirlist[0]
try:
test_rp = dir_rp.append(filename)
except OSError:
return 0
assert test_rp.lstat(), test_rp
period = filename + '.'
if period in dirlist: return 0
period_rp = dir_rp.append(period)
if period_rp.lstat(): return 1
return 0
dirlist = robust.listrp(rp)
if len(dirlist):
self.escape_trailing_spaces = test_period(rp, dirlist)
else:
log.Log("Warning: could not determine if source directory at\n "
+ rp.path + "\npermits trailing spaces or periods in "
"filenames because we can't find any files.\n"
"It will be treated as permitting such files.", 2)
self.escape_trailing_spaces = 0
def get_readonly_fsa(desc_string, rp): def get_readonly_fsa(desc_string, rp):
...@@ -709,6 +665,14 @@ class SetGlobals: ...@@ -709,6 +665,14 @@ class SetGlobals:
SetConnections.UpdateGlobal('symlink_perms', SetConnections.UpdateGlobal('symlink_perms',
self.dest_fsa.symlink_perms) self.dest_fsa.symlink_perms)
def set_escape_dos_devices(self):
SetConnections.UpdateGlobal('escape_dos_devices', \
self.dest_fsa.escape_dos_devices)
def set_escape_trailing_spaces(self):
SetConnections.UpdateGlobal('escape_trailing_spaces', \
self.dest_fsa.escape_trailing_spaces)
class BackupSetGlobals(SetGlobals): class BackupSetGlobals(SetGlobals):
"""Functions for setting fsa related globals for backup session""" """Functions for setting fsa related globals for backup session"""
def update_triple(self, src_support, dest_support, attr_triple): def update_triple(self, src_support, dest_support, attr_triple):
...@@ -723,50 +687,43 @@ class BackupSetGlobals(SetGlobals): ...@@ -723,50 +687,43 @@ class BackupSetGlobals(SetGlobals):
SetConnections.UpdateGlobal(write_attr, 1) SetConnections.UpdateGlobal(write_attr, 1)
self.out_conn.Globals.set_local(conn_attr, 1) self.out_conn.Globals.set_local(conn_attr, 1)
def set_special_escapes(self, rbdir): def set_must_escape_dos_devices(self, rbdir):
"""Escaping DOS devices and trailing periods/spaces works like """If local edd or src edd, then must escape """
regular filename escaping. If only the destination requires it, try:
then we do it. Otherwise, it is not necessary, since the files device_rp = rbdir.append("con")
couldn't have been created in the first place. We also record if device_rp.lstat(): local_edd = 1
whether we have done it in order to handle the case where a else: local_edd = 0
volume which was escaped is later restored by an OS that does except (OSError): local_edd = 1
not require it.""" SetConnections.UpdateGlobal('must_escape_dos_devices', \
self.src_fsa.escape_dos_devices or local_edd)
suggested_edd = (self.dest_fsa.escape_dos_devices and not \ log.Log("Backup: must_escape_dos_devices = %d" % \
self.src_fsa.escape_dos_devices) (self.src_fsa.escape_dos_devices or local_edd), 4)
suggested_ets = (self.dest_fsa.escape_trailing_spaces and not \
self.src_fsa.escape_trailing_spaces) def set_must_escape_trailing_spaces(self, rbdir):
"""If local ets or src ets, then must escape """
se_rp = rbdir.append("special_escapes") # Disable this for 1.2.4
if not se_rp.lstat(): SetConnections.UpdateGlobal('must_escape_trailing_spaces', 0)
actual_edd, actual_ets = suggested_edd, suggested_ets return
se = ""
if actual_edd: se = se + "escape_dos_devices\n"
if actual_ets: se = se + "escape_trailing_spaces\n"
se_rp.write_string(se)
else:
se = se_rp.get_data().split("\n")
actual_edd = ("escape_dos_devices" in se)
actual_ets = ("escape_trailing_spaces" in se)
if actual_edd != suggested_edd and not suggested_edd:
log.Log("Warning: System no longer needs DOS devices escaped, "
"but we will retain for backwards compatibility.", 2)
if actual_ets != suggested_ets and not suggested_ets:
log.Log("Warning: System no longer needs trailing spaces or "
"periods escaped, but we will retain for backwards "
"compatibility.", 2)
SetConnections.UpdateGlobal('escape_dos_devices', actual_edd)
log.Log("Backup: escape_dos_devices = %d" % actual_edd, 4)
SetConnections.UpdateGlobal('escape_trailing_spaces', actual_ets) try:
log.Log("Backup: escape_trailing_spaces = %d" % actual_ets, 4) space_rp = rbdir.append("test ")
space_rp.touch()
if space_rp.lstat():
local_ets = 0
space_rp.delete()
else:
local_ets = 1
except (OSError, IOError):
local_ets = 1
SetConnections.UpdateGlobal('must_escape_trailing_spaces', \
self.src_fsa.escape_trailing_spaces or local_ets)
log.Log("Backup: must_escape_trailing_spaces = %d" % \
(self.src_fsa.escape_trailing_spaces or local_ets), 4)
def set_chars_to_quote(self, rbdir, force): def set_chars_to_quote(self, rbdir, force):
"""Set chars_to_quote setting for backup session """Set chars_to_quote setting for backup session
Unlike most other options, the chars_to_quote setting also Unlike the other options, the chars_to_quote setting also
depends on the current settings in the rdiff-backup-data depends on the current settings in the rdiff-backup-data
directory, not just the current fs features. directory, not just the current fs features.
...@@ -861,39 +818,45 @@ class RestoreSetGlobals(SetGlobals): ...@@ -861,39 +818,45 @@ class RestoreSetGlobals(SetGlobals):
self.out_conn.Globals.set_local(write_attr, 1) self.out_conn.Globals.set_local(write_attr, 1)
if src_support: self.in_conn.Globals.set_local(conn_attr, 1) if src_support: self.in_conn.Globals.set_local(conn_attr, 1)
def set_special_escapes(self, rbdir): def set_must_escape_dos_devices(self, rbdir):
"""Set escape_dos_devices and escape_trailing_spaces from """If local edd or src edd, then must escape """
rdiff-backup-data dir, just like chars_to_quote""" if getattr(self, "src_fsa", None) is not None:
se_rp = rbdir.append("special_escapes") src_edd = self.src_fsa.escape_dos_devices
if se_rp.lstat(): else: src_edd = 0
se = se_rp.get_data().split("\n") try:
actual_edd = ("escape_dos_devices" in se) device_rp = rbdir.append("con")
actual_ets = ("escape_trailing_spaces" in se) if device_rp.lstat(): local_edd = 1
else: local_edd = 0
except (OSError): local_edd = 1
SetConnections.UpdateGlobal('must_escape_dos_devices', \
src_edd or local_edd)
log.Log("Restore: must_escape_dos_devices = %d" % \
(src_edd or local_edd), 4)
def set_must_escape_trailing_spaces(self, rbdir):
"""If local ets or src ets, then must escape """
# Disable this for 1.2.4
SetConnections.UpdateGlobal('must_escape_trailing_spaces', 0)
return
if getattr(self, "src_fsa", None) is not None:
src_ets = self.src_fsa.escape_trailing_spaces
else: else:
log.Log("Warning: special_escapes file not found,\n" src_ets = 0
"will assume need to escape DOS devices and trailing " try:
"spaces based on file systems.", 2) space_rp = rbdir.append("test ")
if getattr(self, "src_fsa", None) is not None: space_rp.touch()
actual_edd = (self.src_fsa.escape_dos_devices and not if space_rp.lstat():
self.dest_fsa.escape_dos_devices) local_ets = 0
try: space_rp.delete()
actual_ets = (self.src_fsa.escape_trailing_spaces and not
self.dest_fsa.escape_trailing_spaces)
except AttributeError: # ETS fixed in version 1.2.5
actual_ets = 0
else: else:
# Single filesystem operation local_ets = 1
actual_edd = self.dest_fsa.escape_dos_devices except (OSError, IOError):
try: local_ets = 1
actual_ets = self.dest_fsa.escape_trailing_spaces SetConnections.UpdateGlobal('must_escape_trailing_spaces', \
except AttributeError: src_ets or local_ets)
actual_ets = 0 log.Log("Restore: must_escape_trailing_spaces = %d" % \
(src_ets or local_ets), 4)
SetConnections.UpdateGlobal('escape_dos_devices', actual_edd)
log.Log("Backup: escape_dos_devices = %d" % actual_edd, 4)
SetConnections.UpdateGlobal('escape_trailing_spaces', actual_ets)
log.Log("Backup: escape_trailing_spaces = %d" % actual_ets, 4)
def set_chars_to_quote(self, rbdir): def set_chars_to_quote(self, rbdir):
"""Set chars_to_quote from rdiff-backup-data dir""" """Set chars_to_quote from rdiff-backup-data dir"""
...@@ -968,7 +931,10 @@ def backup_set_globals(rpin, force): ...@@ -968,7 +931,10 @@ def backup_set_globals(rpin, force):
bsg.set_high_perms() bsg.set_high_perms()
bsg.set_symlink_perms() bsg.set_symlink_perms()
update_quoting = bsg.set_chars_to_quote(Globals.rbdir, force) update_quoting = bsg.set_chars_to_quote(Globals.rbdir, force)
bsg.set_special_escapes(Globals.rbdir) bsg.set_escape_dos_devices()
bsg.set_escape_trailing_spaces()
bsg.set_must_escape_dos_devices(Globals.rbdir)
bsg.set_must_escape_trailing_spaces(Globals.rbdir)
if update_quoting and force: if update_quoting and force:
FilenameMapping.update_quoting(Globals.rbdir) FilenameMapping.update_quoting(Globals.rbdir)
...@@ -995,7 +961,10 @@ def restore_set_globals(rpout): ...@@ -995,7 +961,10 @@ def restore_set_globals(rpout):
rsg.set_high_perms() rsg.set_high_perms()
rsg.set_symlink_perms() rsg.set_symlink_perms()
rsg.set_chars_to_quote(Globals.rbdir) rsg.set_chars_to_quote(Globals.rbdir)
rsg.set_special_escapes(Globals.rbdir) rsg.set_escape_dos_devices()
rsg.set_escape_trailing_spaces()
rsg.set_must_escape_dos_devices(Globals.rbdir)
rsg.set_must_escape_trailing_spaces(Globals.rbdir)
def single_set_globals(rp, read_only = None): def single_set_globals(rp, read_only = None):
"""Set fsa related globals for operation on single filesystem""" """Set fsa related globals for operation on single filesystem"""
...@@ -1015,5 +984,8 @@ def single_set_globals(rp, read_only = None): ...@@ -1015,5 +984,8 @@ def single_set_globals(rp, read_only = None):
ssg.set_high_perms() ssg.set_high_perms()
ssg.set_symlink_perms() ssg.set_symlink_perms()
ssg.set_chars_to_quote(Globals.rbdir) ssg.set_chars_to_quote(Globals.rbdir)
ssg.set_special_escapes(Globals.rbdir) ssg.set_escape_dos_devices()
ssg.set_escape_trailing_spaces()
ssg.set_must_escape_dos_devices(Globals.rbdir)
ssg.set_must_escape_trailing_spaces(Globals.rbdir)
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