From 9eba7fcf2868607443f267f4c8fe7cbe576c3bdb Mon Sep 17 00:00:00 2001 From: owsla Date: Thu, 13 Nov 2008 00:15:56 +0000 Subject: Escape trailing spaces and periods on systems which require it git-svn-id: http://svn.savannah.nongnu.org/svn/rdiff-backup/trunk@961 2b77aa54-bcbc-44c9-a7ec-4f6cf2b41109 --- rdiff-backup/CHANGELOG | 3 ++ rdiff-backup/rdiff_backup/FilenameMapping.py | 13 +++++ rdiff-backup/rdiff_backup/fs_abilities.py | 71 +++++++++++++++++++++++++++- 3 files changed, 86 insertions(+), 1 deletion(-) diff --git a/rdiff-backup/CHANGELOG b/rdiff-backup/CHANGELOG index 68f9382..917fc50 100644 --- a/rdiff-backup/CHANGELOG +++ b/rdiff-backup/CHANGELOG @@ -1,6 +1,9 @@ New in v1.2.3 (????/??/??) --------------------------- +Escape trailing spaces and periods on systems which require it, such as +Windows and modern Linux with FAT32. (Andrew Ferguson) + Print nicer error messages in rdiff-backup-statistics (without tracebacks). Closes Ubuntu bug #292586. (Andrew Ferguson) diff --git a/rdiff-backup/rdiff_backup/FilenameMapping.py b/rdiff-backup/rdiff_backup/FilenameMapping.py index 5fbd9dd..439836e 100644 --- a/rdiff-backup/rdiff_backup/FilenameMapping.py +++ b/rdiff-backup/rdiff_backup/FilenameMapping.py @@ -85,6 +85,19 @@ def quote(path): """ QuotedPath = chars_to_quote_regexp.sub(quote_single, path) + if not Globals.must_escape_dos_devices \ + and not Globals.must_escape_trailing_spaces: + return QuotedPath + + if Globals.must_escape_dos_devices: + assert Globals.must_escape_trailing_spaces + + # Escape a trailing space or period (invalid in names on FAT32 under DOS, + # Windows and modern Linux) + if len(QuotedPath) and (QuotedPath[-1] == ' ' or QuotedPath[-1] == '.'): + QuotedPath = QuotedPath[:-1] + \ + "%s%03d" % (quoting_char, ord(QuotedPath[-1])) + if not Globals.must_escape_dos_devices: return QuotedPath diff --git a/rdiff-backup/rdiff_backup/fs_abilities.py b/rdiff-backup/rdiff_backup/fs_abilities.py index 73a0cb8..a434f6e 100644 --- a/rdiff-backup/rdiff_backup/fs_abilities.py +++ b/rdiff-backup/rdiff_backup/fs_abilities.py @@ -50,6 +50,8 @@ class FSAbilities: 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., # aux, con, com1, etc) + escape_trailing_spaces = None # True if files with trailing spaces or + # periods can't be created symlink_perms = None # True if symlink perms are affected by umask def __init__(self, name = None): @@ -101,6 +103,8 @@ class FSAbilities: ('Windows access control lists', self.win_acls), ('Case sensitivity', self.case_sensitive), ('Escape DOS devices', self.escape_dos_devices), + ('Escape trailing spaces', + self.escape_trailing_spaces) ('Mac OS X style resource forks', self.resource_forks), ('Mac OS X Finder information', self.carbonfile)]) @@ -127,6 +131,7 @@ class FSAbilities: self.set_carbonfile() self.set_case_sensitive_readonly(rp) self.set_escape_dos_devices(rp) + self.set_escape_trailing_spaces(rp) return self def init_readwrite(self, rbdir): @@ -161,6 +166,7 @@ class FSAbilities: self.set_high_perms_readwrite(subdir) self.set_symlink_perms(subdir) self.set_escape_dos_devices(subdir) + self.set_escape_trailing_spaces(subdir) subdir.delete() return self @@ -550,7 +556,7 @@ class FSAbilities: sym_source.delete() def set_escape_dos_devices(self, subdir): - """If special file aux can be stat'd, escape special files""" + """If special file con can be stat'd, escape special files""" try: device_rp = subdir.append("con") if device_rp.lstat(): @@ -566,6 +572,25 @@ class FSAbilities: % (subdir.path), 4) self.escape_dos_devices = 1 + def set_escape_trailing_spaces(self, subdir): + """If file with trailing space can't be created, escape such files""" + try: + space_rp = subdir.append("test ") + if space_rp.touch() and space_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): + log.Log("escape_trailing_spaces required by filesystem at %s" \ + % (subdir.path), 4) + self.escape_trailing_spaces = 1 + + def get_readonly_fsa(desc_string, rp): """Return an fsa with given description_string @@ -641,6 +666,10 @@ class SetGlobals: 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): """Functions for setting fsa related globals for backup session""" def update_triple(self, src_support, dest_support, attr_triple): @@ -667,6 +696,21 @@ class BackupSetGlobals(SetGlobals): log.Log("Backup: must_escape_dos_devices = %d" % \ (self.src_fsa.escape_dos_devices or local_edd), 4) + def set_must_escape_trailing_spaces(self, rbdir): + """If local ets or src ets, then must escape """ + try: + space_rp = rbdir.append("test ") + if space_rp.touch() and space_rp.lstat(): + local_ets = 0 + space_rp.delete() + else: + local_ets = 1 + except (OSError): 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): """Set chars_to_quote setting for backup session @@ -779,6 +823,25 @@ class RestoreSetGlobals(SetGlobals): 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 """ + if getattr(self, "src_fsa", None) is not None: + src_ets = self.src_fsa.escape_trailing_spaces + else: + src_ets = 0 + try: + space_rp = rbdir.append("test ") + if space_rp.touch() and space_rp.lstat(): + local_ets = 0 + space_rp.delete() + else: + local_ets = 1 + except (OSError): local_ets = 1 + SetConnections.UpdateGlobal('must_escape_trailing_spaces', \ + src_ets or local_ets) + log.Log("Restore: must_escape_trailing_spaces = %d" % \ + (src_ets or local_ets), 4) + def set_chars_to_quote(self, rbdir): """Set chars_to_quote from rdiff-backup-data dir""" if Globals.chars_to_quote is not None: return # already overridden @@ -853,7 +916,9 @@ def backup_set_globals(rpin, force): bsg.set_symlink_perms() update_quoting = bsg.set_chars_to_quote(Globals.rbdir, force) 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: FilenameMapping.update_quoting(Globals.rbdir) @@ -881,7 +946,9 @@ def restore_set_globals(rpout): rsg.set_symlink_perms() rsg.set_chars_to_quote(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): """Set fsa related globals for operation on single filesystem""" @@ -902,5 +969,7 @@ def single_set_globals(rp, read_only = None): ssg.set_symlink_perms() ssg.set_chars_to_quote(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) -- cgit v1.2.1